blob: 1c63e4a3a65b6d55b3e440d6f2e4b4bdc41d4c65 [file] [log] [blame]
/* ld.so error exception allocation and deallocation.
Copyright (C) 1995-2018 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
#include <ldsodefs.h>
#include <limits.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
/* This message we return as a last resort. We define the string in a
variable since we have to avoid freeing it and so have to enable
a pointer comparison. See below and in dlfcn/dlerror.c. */
static const char _dl_out_of_memory[] = "out of memory";
/* Dummy allocation object used if allocating the message buffer
fails. */
static void
oom_exception (struct dl_exception *exception)
{
exception->objname = "";
exception->errstring = _dl_out_of_memory;
exception->message_buffer = NULL;
}
static void
__attribute__ ((noreturn))
length_mismatch (void)
{
_dl_fatal_printf ("Fatal error: "
"length accounting in _dl_exception_create_format\n");
}
/* Adjust the message buffer to indicate whether it is possible to
free it. EXCEPTION->errstring must be a potentially deallocatable
pointer. */
static void
adjust_message_buffer (struct dl_exception *exception)
{
/* If the main executable is relocated it means the libc's malloc
is used. */
bool malloced = true;
#ifdef SHARED
malloced = (GL(dl_ns)[LM_ID_BASE]._ns_loaded != NULL
&& (GL(dl_ns)[LM_ID_BASE]._ns_loaded->l_relocated != 0));
#endif
if (malloced)
exception->message_buffer = (char *) exception->errstring;
else
exception->message_buffer = NULL;
}
void
_dl_exception_create (struct dl_exception *exception, const char *objname,
const char *errstring)
{
if (objname == NULL)
objname = "";
size_t len_objname = strlen (objname) + 1;
size_t len_errstring = strlen (errstring) + 1;
char *errstring_copy = malloc (len_objname + len_errstring);
if (errstring_copy != NULL)
{
/* Make a copy of the object file name and the error string. */
exception->objname = memcpy (__mempcpy (errstring_copy,
errstring, len_errstring),
objname, len_objname);
exception->errstring = errstring_copy;
adjust_message_buffer (exception);
}
else
oom_exception (exception);
}
rtld_hidden_def (_dl_exception_create)
void
_dl_exception_create_format (struct dl_exception *exception, const char *objname,
const char *fmt, ...)
{
if (objname == NULL)
objname = "";
size_t len_objname = strlen (objname) + 1;
/* Compute the length of the result. Include room for two NUL
bytes. */
size_t length = len_objname + 1;
{
va_list ap;
va_start (ap, fmt);
for (const char *p = fmt; *p != '\0'; ++p)
if (*p == '%')
{
++p;
switch (*p)
{
case 's':
length += strlen (va_arg (ap, const char *));
break;
default:
/* Assumed to be '%'. */
++length;
break;
}
}
else
++length;
va_end (ap);
}
if (length > PTRDIFF_MAX)
{
oom_exception (exception);
return;
}
char *errstring = malloc (length);
if (errstring == NULL)
{
oom_exception (exception);
return;
}
exception->errstring = errstring;
adjust_message_buffer (exception);
/* Copy the error message to errstring. */
{
/* Next byte to be written in errstring. */
char *wptr = errstring;
/* End of the allocated string. */
char *const end = errstring + length;
va_list ap;
va_start (ap, fmt);
for (const char *p = fmt; *p != '\0'; ++p)
if (*p == '%')
{
++p;
switch (*p)
{
case 's':
{
const char *ptr = va_arg (ap, const char *);
size_t len_ptr = strlen (ptr);
if (len_ptr > end - wptr)
length_mismatch ();
wptr = __mempcpy (wptr, ptr, len_ptr);
}
break;
case '%':
if (wptr == end)
length_mismatch ();
*wptr = '%';
++wptr;
break;
default:
_dl_fatal_printf ("Fatal error:"
" invalid format in exception string\n");
}
}
else
{
if (wptr == end)
length_mismatch ();
*wptr = *p;
++wptr;
}
if (wptr == end)
length_mismatch ();
*wptr = '\0';
++wptr;
if (len_objname != end - wptr)
length_mismatch ();
exception->objname = memcpy (wptr, objname, len_objname);
}
}
rtld_hidden_def (_dl_exception_create_format)
void
_dl_exception_free (struct dl_exception *exception)
{
free (exception->message_buffer);
exception->objname = NULL;
exception->errstring = NULL;
exception->message_buffer = NULL;
}
rtld_hidden_def (_dl_exception_free)