/* 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 . */ #include #include #include #include #include #include #include /* 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; /* Recognize the l modifier. It is only important on some platforms where long and int have a different size. We can use the same code for size_t. */ case 'l': case 'z': if (p[1] == 'x') { length += LONG_WIDTH / 4; ++p; break; } case 'x': length += INT_WIDTH / 4; 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; case 'x': { unsigned long int num = va_arg (ap, unsigned int); char *start = wptr; wptr += INT_WIDTH / 4; char *cp = _itoa (num, wptr, 16, 0); /* Pad to the full width with 0. */ while (cp != start) *--cp = '0'; } break; case 'l': case 'z': if (p[1] == 'x') { unsigned long int num = va_arg (ap, unsigned long int); char *start = wptr; wptr += LONG_WIDTH / 4; char *cp = _itoa (num, wptr, 16, 0); /* Pad to the full width with 0. */ while (cp != start) *--cp = '0'; ++p; break; } /* FALLTHROUGH */ 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)