mirror of
https://sourceware.org/git/glibc.git
synced 2024-11-21 12:30:06 +00:00
dlfcn: dlerror needs to call free from the base namespace [BZ #24773]
Calling free directly may end up freeing a pointer allocated by the dynamic loader using malloc from libc.so in the base namespace using the allocator from libc.so in a secondary namespace, which results in crashes. This commit redirects the free call through GLRO and the dynamic linker, to reach the correct namespace. It also cleans up the dlerror handling along the way, so that pthread_setspecific is no longer needed (which avoids triggering bug 24774).
This commit is contained in:
parent
b2964eb1d9
commit
fada901819
@ -22,9 +22,10 @@ include ../Makeconfig
|
||||
headers := bits/dlfcn.h dlfcn.h
|
||||
extra-libs := libdl
|
||||
libdl-routines := dlopen dlclose dlsym dlvsym dlerror dladdr dladdr1 dlinfo \
|
||||
dlmopen dlfcn dlfreeres
|
||||
dlmopen dlfcn
|
||||
routines := $(patsubst %,s%,$(filter-out dlfcn,$(libdl-routines)))
|
||||
elide-routines.os := $(routines)
|
||||
routines += libc_dlerror_result
|
||||
|
||||
extra-libs-others := libdl
|
||||
|
||||
|
@ -1,3 +1,8 @@
|
||||
libc {
|
||||
GLIBC_PRIVATE {
|
||||
__libc_dlerror_result;
|
||||
}
|
||||
}
|
||||
libdl {
|
||||
GLIBC_2.0 {
|
||||
dladdr; dlclose; dlerror; dlopen; dlsym;
|
||||
@ -13,6 +18,5 @@ libdl {
|
||||
}
|
||||
GLIBC_PRIVATE {
|
||||
_dlfcn_hook;
|
||||
__libdl_freeres;
|
||||
}
|
||||
}
|
||||
|
319
dlfcn/dlerror.c
319
dlfcn/dlerror.c
@ -25,6 +25,8 @@
|
||||
#include <libc-lock.h>
|
||||
#include <ldsodefs.h>
|
||||
#include <libc-symbols.h>
|
||||
#include <assert.h>
|
||||
#include <dlerror.h>
|
||||
|
||||
#if !defined SHARED && IS_IN (libdl)
|
||||
|
||||
@ -36,92 +38,75 @@ dlerror (void)
|
||||
|
||||
#else
|
||||
|
||||
/* Type for storing results of dynamic loading actions. */
|
||||
struct dl_action_result
|
||||
{
|
||||
int errcode;
|
||||
int returned;
|
||||
bool malloced;
|
||||
const char *objname;
|
||||
const char *errstring;
|
||||
};
|
||||
static struct dl_action_result last_result;
|
||||
static struct dl_action_result *static_buf;
|
||||
|
||||
/* This is the key for the thread specific memory. */
|
||||
static __libc_key_t key;
|
||||
__libc_once_define (static, once);
|
||||
|
||||
/* Destructor for the thread-specific data. */
|
||||
static void init (void);
|
||||
static void free_key_mem (void *mem);
|
||||
|
||||
|
||||
char *
|
||||
__dlerror (void)
|
||||
{
|
||||
char *buf = NULL;
|
||||
struct dl_action_result *result;
|
||||
|
||||
# ifdef SHARED
|
||||
if (!rtld_active ())
|
||||
return _dlfcn_hook->dlerror ();
|
||||
# endif
|
||||
|
||||
/* If we have not yet initialized the buffer do it now. */
|
||||
__libc_once (once, init);
|
||||
struct dl_action_result *result = __libc_dlerror_result;
|
||||
|
||||
/* Get error string. */
|
||||
if (static_buf != NULL)
|
||||
result = static_buf;
|
||||
/* No libdl function has been called. No error is possible. */
|
||||
if (result == NULL)
|
||||
return NULL;
|
||||
|
||||
/* For an early malloc failure, clear the error flag and return the
|
||||
error message. This marks the error as delivered. */
|
||||
if (result == dl_action_result_malloc_failed)
|
||||
{
|
||||
__libc_dlerror_result = NULL;
|
||||
return (char *) "out of memory";
|
||||
}
|
||||
|
||||
/* Placeholder object. This can be observed in a recursive call,
|
||||
e.g. from an ELF constructor. */
|
||||
if (result->errstring == NULL)
|
||||
return NULL;
|
||||
|
||||
/* If we have already reported the error, we can free the result and
|
||||
return NULL. See __libc_dlerror_result_free. */
|
||||
if (result->returned)
|
||||
{
|
||||
__libc_dlerror_result = NULL;
|
||||
dl_action_result_errstring_free (result);
|
||||
free (result);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
assert (result->errstring != NULL);
|
||||
|
||||
/* Create the combined error message. */
|
||||
char *buf;
|
||||
int n;
|
||||
if (result->errcode == 0)
|
||||
n = __asprintf (&buf, "%s%s%s",
|
||||
result->objname,
|
||||
result->objname[0] == '\0' ? "" : ": ",
|
||||
_(result->errstring));
|
||||
else
|
||||
{
|
||||
/* init () has been run and we don't use the static buffer.
|
||||
So we have a valid key. */
|
||||
result = (struct dl_action_result *) __libc_getspecific (key);
|
||||
if (result == NULL)
|
||||
result = &last_result;
|
||||
}
|
||||
n = __asprintf (&buf, "%s%s%s: %s",
|
||||
result->objname,
|
||||
result->objname[0] == '\0' ? "" : ": ",
|
||||
_(result->errstring),
|
||||
strerror (result->errcode));
|
||||
|
||||
/* Test whether we already returned the string. */
|
||||
if (result->returned != 0)
|
||||
{
|
||||
/* We can now free the string. */
|
||||
if (result->errstring != NULL)
|
||||
{
|
||||
if (strcmp (result->errstring, "out of memory") != 0)
|
||||
free ((char *) result->errstring);
|
||||
result->errstring = NULL;
|
||||
}
|
||||
}
|
||||
else if (result->errstring != NULL)
|
||||
{
|
||||
buf = (char *) result->errstring;
|
||||
int n;
|
||||
if (result->errcode == 0)
|
||||
n = __asprintf (&buf, "%s%s%s",
|
||||
result->objname,
|
||||
result->objname[0] == '\0' ? "" : ": ",
|
||||
_(result->errstring));
|
||||
else
|
||||
n = __asprintf (&buf, "%s%s%s: %s",
|
||||
result->objname,
|
||||
result->objname[0] == '\0' ? "" : ": ",
|
||||
_(result->errstring),
|
||||
strerror (result->errcode));
|
||||
if (n != -1)
|
||||
{
|
||||
/* We don't need the error string anymore. */
|
||||
if (strcmp (result->errstring, "out of memory") != 0)
|
||||
free ((char *) result->errstring);
|
||||
result->errstring = buf;
|
||||
}
|
||||
/* Mark the error as delivered. */
|
||||
result->returned = true;
|
||||
|
||||
/* Mark the error as returned. */
|
||||
result->returned = 1;
|
||||
if (n >= 0)
|
||||
{
|
||||
/* Replace the error string with the newly allocated one. */
|
||||
dl_action_result_errstring_free (result);
|
||||
result->errstring = buf;
|
||||
result->errstring_source = dl_action_result_errstring_local;
|
||||
return buf;
|
||||
}
|
||||
|
||||
return buf;
|
||||
else
|
||||
/* We could not create the combined error message, so use the
|
||||
existing string as a fallback. */
|
||||
return result->errstring;
|
||||
}
|
||||
# ifdef SHARED
|
||||
strong_alias (__dlerror, dlerror)
|
||||
@ -130,130 +115,94 @@ strong_alias (__dlerror, dlerror)
|
||||
int
|
||||
_dlerror_run (void (*operate) (void *), void *args)
|
||||
{
|
||||
struct dl_action_result *result;
|
||||
struct dl_action_result *result = __libc_dlerror_result;
|
||||
if (result != NULL)
|
||||
{
|
||||
if (result == dl_action_result_malloc_failed)
|
||||
{
|
||||
/* Clear the previous error. */
|
||||
__libc_dlerror_result = NULL;
|
||||
result = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* There is an existing object. Free its error string, but
|
||||
keep the object. */
|
||||
dl_action_result_errstring_free (result);
|
||||
/* Mark the object as not containing an error. This ensures
|
||||
that call to dlerror from, for example, an ELF
|
||||
constructor will not notice this result object. */
|
||||
result->errstring = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* If we have not yet initialized the buffer do it now. */
|
||||
__libc_once (once, init);
|
||||
const char *objname;
|
||||
const char *errstring;
|
||||
bool malloced;
|
||||
int errcode = GLRO (dl_catch_error) (&objname, &errstring, &malloced,
|
||||
operate, args);
|
||||
|
||||
/* Get error string and number. */
|
||||
if (static_buf != NULL)
|
||||
result = static_buf;
|
||||
/* ELF constructors or destructors may have indirectly altered the
|
||||
value of __libc_dlerror_result, therefore reload it. */
|
||||
result = __libc_dlerror_result;
|
||||
|
||||
if (errstring == NULL)
|
||||
{
|
||||
/* There is no error. We no longer need the result object if it
|
||||
does not contain an error. However, a recursive call may
|
||||
have added an error even if this call did not cause it. Keep
|
||||
the other error. */
|
||||
if (result != NULL && result->errstring == NULL)
|
||||
{
|
||||
__libc_dlerror_result = NULL;
|
||||
free (result);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* We don't use the static buffer and so we have a key. Use it
|
||||
to get the thread-specific buffer. */
|
||||
result = __libc_getspecific (key);
|
||||
if (result == NULL)
|
||||
/* A new error occurred. Check if a result object has to be
|
||||
allocated. */
|
||||
if (result == NULL || result == dl_action_result_malloc_failed)
|
||||
{
|
||||
result = (struct dl_action_result *) calloc (1, sizeof (*result));
|
||||
/* Allocating storage for the error message after the fact
|
||||
is not ideal. But this avoids an infinite recursion in
|
||||
case malloc itself calls libdl functions (without
|
||||
triggering errors). */
|
||||
result = malloc (sizeof (*result));
|
||||
if (result == NULL)
|
||||
/* We are out of memory. Since this is no really critical
|
||||
situation we carry on by using the global variable.
|
||||
This might lead to conflicts between the threads but
|
||||
they soon all will have memory problems. */
|
||||
result = &last_result;
|
||||
else
|
||||
/* Set the tsd. */
|
||||
__libc_setspecific (key, result);
|
||||
{
|
||||
/* Assume that the dlfcn failure was due to a malloc
|
||||
failure, too. */
|
||||
if (malloced)
|
||||
dl_error_free ((char *) errstring);
|
||||
__libc_dlerror_result = dl_action_result_malloc_failed;
|
||||
return 1;
|
||||
}
|
||||
__libc_dlerror_result = result;
|
||||
}
|
||||
else
|
||||
/* Deallocate the existing error message from a recursive
|
||||
call, but reuse the result object. */
|
||||
dl_action_result_errstring_free (result);
|
||||
|
||||
result->errcode = errcode;
|
||||
result->objname = objname;
|
||||
result->errstring = (char *) errstring;
|
||||
result->returned = false;
|
||||
/* In case of an error, the malloced flag indicates whether the
|
||||
error string is constant or not. */
|
||||
if (malloced)
|
||||
result->errstring_source = dl_action_result_errstring_rtld;
|
||||
else
|
||||
result->errstring_source = dl_action_result_errstring_constant;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (result->errstring != NULL)
|
||||
{
|
||||
/* Free the error string from the last failed command. This can
|
||||
happen if `dlerror' was not run after an error was found. */
|
||||
if (result->malloced)
|
||||
free ((char *) result->errstring);
|
||||
result->errstring = NULL;
|
||||
}
|
||||
|
||||
result->errcode = GLRO (dl_catch_error) (&result->objname,
|
||||
&result->errstring,
|
||||
&result->malloced,
|
||||
operate, args);
|
||||
|
||||
/* If no error we mark that no error string is available. */
|
||||
result->returned = result->errstring == NULL;
|
||||
|
||||
return result->errstring != NULL;
|
||||
}
|
||||
|
||||
|
||||
/* Initialize buffers for results. */
|
||||
static void
|
||||
init (void)
|
||||
{
|
||||
if (__libc_key_create (&key, free_key_mem))
|
||||
/* Creating the key failed. This means something really went
|
||||
wrong. In any case use a static buffer which is better than
|
||||
nothing. */
|
||||
static_buf = &last_result;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
check_free (struct dl_action_result *rec)
|
||||
{
|
||||
if (rec->errstring != NULL
|
||||
&& strcmp (rec->errstring, "out of memory") != 0)
|
||||
{
|
||||
/* We can free the string only if the allocation happened in the
|
||||
C library used by the dynamic linker. This means, it is
|
||||
always the C library in the base namespace. When we're statically
|
||||
linked, the dynamic linker is part of the program and so always
|
||||
uses the same C library we use here. */
|
||||
#ifdef SHARED
|
||||
struct link_map *map = NULL;
|
||||
Dl_info info;
|
||||
if (_dl_addr (check_free, &info, &map, NULL) != 0 && map->l_ns == 0)
|
||||
#endif
|
||||
{
|
||||
free ((char *) rec->errstring);
|
||||
rec->errstring = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
__attribute__ ((destructor))
|
||||
fini (void)
|
||||
{
|
||||
check_free (&last_result);
|
||||
}
|
||||
|
||||
|
||||
/* Free the thread specific data, this is done if a thread terminates. */
|
||||
static void
|
||||
free_key_mem (void *mem)
|
||||
{
|
||||
check_free ((struct dl_action_result *) mem);
|
||||
|
||||
free (mem);
|
||||
__libc_setspecific (key, NULL);
|
||||
}
|
||||
|
||||
# ifdef SHARED
|
||||
|
||||
/* Free the dlerror-related resources. */
|
||||
void
|
||||
__dlerror_main_freeres (void)
|
||||
{
|
||||
/* Free the global memory if used. */
|
||||
check_free (&last_result);
|
||||
|
||||
if (__libc_once_get (once) && static_buf == NULL)
|
||||
{
|
||||
/* init () has been run and we don't use the static buffer.
|
||||
So we have a valid key. */
|
||||
void *mem;
|
||||
/* Free the TSD memory if used. */
|
||||
mem = __libc_getspecific (key);
|
||||
if (mem != NULL)
|
||||
free_key_mem (mem);
|
||||
}
|
||||
}
|
||||
|
||||
struct dlfcn_hook *_dlfcn_hook __attribute__((nocommon));
|
||||
libdl_hidden_data_def (_dlfcn_hook)
|
||||
|
||||
|
92
dlfcn/dlerror.h
Normal file
92
dlfcn/dlerror.h
Normal file
@ -0,0 +1,92 @@
|
||||
/* Memory management for dlerror messages.
|
||||
Copyright (C) 2021 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
|
||||
<https://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef _DLERROR_H
|
||||
#define _DLERROR_H
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <ldsodefs.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/* Source of the errstring member in struct dl_action_result, for
|
||||
finding the right deallocation routine. */
|
||||
enum dl_action_result_errstring_source
|
||||
{
|
||||
dl_action_result_errstring_constant, /* String literal, no deallocation. */
|
||||
dl_action_result_errstring_rtld, /* libc in the primary namespace. */
|
||||
dl_action_result_errstring_local, /* libc in the current namespace. */
|
||||
};
|
||||
|
||||
struct dl_action_result
|
||||
{
|
||||
int errcode;
|
||||
char errstring_source;
|
||||
bool returned;
|
||||
const char *objname;
|
||||
char *errstring;
|
||||
};
|
||||
|
||||
/* Used to free the errstring member of struct dl_action_result in the
|
||||
dl_action_result_errstring_rtld case. */
|
||||
static inline void
|
||||
dl_error_free (void *ptr)
|
||||
{
|
||||
#ifdef SHARED
|
||||
/* In the shared case, ld.so may use a different malloc than this
|
||||
namespace. */
|
||||
GLRO (dl_error_free (ptr));
|
||||
#else
|
||||
/* Call the implementation directly. It still has to check for
|
||||
pointers which cannot be freed, so do not call free directly
|
||||
here. */
|
||||
_dl_error_free (ptr);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Deallocate RESULT->errstring, leaving *RESULT itself allocated. */
|
||||
static inline void
|
||||
dl_action_result_errstring_free (struct dl_action_result *result)
|
||||
{
|
||||
switch (result->errstring_source)
|
||||
{
|
||||
case dl_action_result_errstring_constant:
|
||||
break;
|
||||
case dl_action_result_errstring_rtld:
|
||||
dl_error_free (result->errstring);
|
||||
break;
|
||||
case dl_action_result_errstring_local:
|
||||
free (result->errstring);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Stand-in for an error result object whose allocation failed. No
|
||||
precise message can be reported for this, but an error must still
|
||||
be signaled. */
|
||||
static struct dl_action_result *const dl_action_result_malloc_failed
|
||||
__attribute__ ((unused)) = (struct dl_action_result *) (intptr_t) -1;
|
||||
|
||||
/* Thread-local variable for storing dlfcn failures for subsequent
|
||||
reporting via dlerror. */
|
||||
extern __thread struct dl_action_result *__libc_dlerror_result
|
||||
attribute_tls_model_ie;
|
||||
void __libc_dlerror_result_free (void) attribute_hidden;
|
||||
|
||||
#endif /* _DLERROR_H */
|
@ -1,29 +0,0 @@
|
||||
/* Clean up allocated libdl memory on demand.
|
||||
Copyright (C) 2018-2021 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
|
||||
<https://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <set-hooks.h>
|
||||
#include <libc-symbols.h>
|
||||
#include <dlfcn.h>
|
||||
|
||||
/* Free libdl.so resources.
|
||||
Note: Caller ensures we are called only once. */
|
||||
void
|
||||
__libdl_freeres (void)
|
||||
{
|
||||
call_function_static_weak (__dlerror_main_freeres);
|
||||
}
|
39
dlfcn/libc_dlerror_result.c
Normal file
39
dlfcn/libc_dlerror_result.c
Normal file
@ -0,0 +1,39 @@
|
||||
/* Thread-local variable holding the dlerror result.
|
||||
Copyright (C) 2021 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 <dlerror.h>
|
||||
|
||||
/* This pointer is either NULL, dl_action_result_malloc_failed (), or
|
||||
has been allocated using malloc by the namespace that also contains
|
||||
this instance of the thread-local variable. */
|
||||
__thread struct dl_action_result *__libc_dlerror_result attribute_tls_model_ie;
|
||||
|
||||
/* Called during thread shutdown to free resources. */
|
||||
void
|
||||
__libc_dlerror_result_free (void)
|
||||
{
|
||||
if (__libc_dlerror_result != NULL)
|
||||
{
|
||||
if (__libc_dlerror_result != dl_action_result_malloc_failed)
|
||||
{
|
||||
dl_action_result_errstring_free (__libc_dlerror_result);
|
||||
free (__libc_dlerror_result);
|
||||
}
|
||||
__libc_dlerror_result = NULL;
|
||||
}
|
||||
}
|
@ -30,6 +30,17 @@
|
||||
a pointer comparison. See below and in dlfcn/dlerror.c. */
|
||||
static const char _dl_out_of_memory[] = "out of memory";
|
||||
|
||||
/* Call free in the main libc.so. This allows other namespaces to
|
||||
free pointers on the main libc heap, via GLRO (dl_error_free). It
|
||||
also avoids calling free on the special, pre-allocated
|
||||
out-of-memory error message. */
|
||||
void
|
||||
_dl_error_free (void *ptr)
|
||||
{
|
||||
if (ptr != _dl_out_of_memory)
|
||||
free (ptr);
|
||||
}
|
||||
|
||||
/* Dummy allocation object used if allocating the message buffer
|
||||
fails. */
|
||||
static void
|
||||
|
@ -369,6 +369,7 @@ struct rtld_global_ro _rtld_global_ro attribute_relro =
|
||||
._dl_open = _dl_open,
|
||||
._dl_close = _dl_close,
|
||||
._dl_catch_error = _rtld_catch_error,
|
||||
._dl_error_free = _dl_error_free,
|
||||
._dl_tls_get_addr_soft = _dl_tls_get_addr_soft,
|
||||
#ifdef HAVE_DL_DISCOVER_OSVERSION
|
||||
._dl_discover_osversion = _dl_discover_osversion
|
||||
|
@ -18,6 +18,8 @@
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <support/check.h>
|
||||
|
||||
/* Note: This object is not linked into the main program, so we cannot
|
||||
@ -25,17 +27,32 @@
|
||||
to use FAIL_EXIT1 (or something else that calls exit). */
|
||||
|
||||
void
|
||||
call_dlsym (void)
|
||||
call_dlsym (const char *name)
|
||||
{
|
||||
void *ptr = dlsym (NULL, "does not exist");
|
||||
void *ptr = dlsym (NULL, name);
|
||||
if (ptr != NULL)
|
||||
FAIL_EXIT1 ("dlsym did not fail as expected");
|
||||
FAIL_EXIT1 ("dlsym did not fail as expected for: %s", name);
|
||||
const char *message = dlerror ();
|
||||
if (strstr (message, ": undefined symbol: does not exist X") == NULL)
|
||||
FAIL_EXIT1 ("invalid dlsym error message for [[%s]]: %s", name, message);
|
||||
message = dlerror ();
|
||||
if (message != NULL)
|
||||
FAIL_EXIT1 ("second dlsym for [[%s]]: %s", name, message);
|
||||
}
|
||||
|
||||
void
|
||||
call_dlopen (void)
|
||||
call_dlopen (const char *name)
|
||||
{
|
||||
void *handle = dlopen ("tst-dlmopen-dlerror does not exist", RTLD_NOW);
|
||||
void *handle = dlopen (name, RTLD_NOW);
|
||||
if (handle != NULL)
|
||||
FAIL_EXIT1 ("dlopen did not fail as expected");
|
||||
FAIL_EXIT1 ("dlopen did not fail as expected for: %s", name);
|
||||
const char *message = dlerror ();
|
||||
if (strstr (message, "X: cannot open shared object file:"
|
||||
" No such file or directory") == NULL
|
||||
&& strstr (message, "X: cannot open shared object file:"
|
||||
" File name too long") == NULL)
|
||||
FAIL_EXIT1 ("invalid dlopen error message for [[%s]]: %s", name, message);
|
||||
message = dlerror ();
|
||||
if (message != NULL)
|
||||
FAIL_EXIT1 ("second dlopen for [[%s]]: %s", name, message);
|
||||
}
|
||||
|
@ -17,6 +17,7 @@
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <support/check.h>
|
||||
#include <support/xdlfcn.h>
|
||||
|
||||
@ -25,11 +26,22 @@ do_test (void)
|
||||
{
|
||||
void *handle = xdlmopen (LM_ID_NEWLM, "tst-dlmopen-dlerror-mod.so",
|
||||
RTLD_NOW);
|
||||
void (*call_dlsym) (void) = xdlsym (handle, "call_dlsym");
|
||||
void (*call_dlopen) (void) = xdlsym (handle, "call_dlopen");
|
||||
void (*call_dlsym) (const char *name) = xdlsym (handle, "call_dlsym");
|
||||
void (*call_dlopen) (const char *name) = xdlsym (handle, "call_dlopen");
|
||||
|
||||
call_dlsym ();
|
||||
call_dlopen ();
|
||||
/* Iterate over various name lengths. This changes the size of
|
||||
error messages allocated by ld.so and has been shown to trigger
|
||||
detectable heap corruption if malloc/free calls in different
|
||||
namespaces are mixed. */
|
||||
char buffer[2048];
|
||||
char *buffer_end = &buffer[sizeof (buffer) - 2];
|
||||
for (char *p = stpcpy (buffer, "does not exist "); p < buffer_end; ++p)
|
||||
{
|
||||
p[0] = 'X';
|
||||
p[1] = '\0';
|
||||
call_dlsym (buffer);
|
||||
call_dlopen (buffer);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -156,7 +156,5 @@ extern void __libc_register_dlfcn_hook (struct link_map *map)
|
||||
attribute_hidden;
|
||||
#endif
|
||||
|
||||
extern void __dlerror_main_freeres (void) attribute_hidden;
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include <set-hooks.h>
|
||||
#include <libc-internal.h>
|
||||
#include <unwind-link.h>
|
||||
#include <dlfcn/dlerror.h>
|
||||
|
||||
#include "../nss/nsswitch.h"
|
||||
#include "../libio/libioP.h"
|
||||
@ -28,8 +29,6 @@ DEFINE_HOOK (__libc_subfreeres, (void));
|
||||
|
||||
symbol_set_define (__libc_freeres_ptrs);
|
||||
|
||||
extern __attribute__ ((weak)) void __libdl_freeres (void);
|
||||
|
||||
extern __attribute__ ((weak)) void __libpthread_freeres (void);
|
||||
|
||||
void __libc_freeres_fn_section
|
||||
@ -52,11 +51,6 @@ __libc_freeres (void)
|
||||
/* We run the resource freeing after IO cleanup. */
|
||||
RUN_HOOK (__libc_subfreeres, ());
|
||||
|
||||
/* Call the libdl list of cleanup functions
|
||||
(weak-ref-and-check). */
|
||||
if (&__libdl_freeres != NULL)
|
||||
__libdl_freeres ();
|
||||
|
||||
/* Call the libpthread list of cleanup functions
|
||||
(weak-ref-and-check). */
|
||||
if (&__libpthread_freeres != NULL)
|
||||
@ -66,6 +60,8 @@ __libc_freeres (void)
|
||||
__libc_unwind_link_freeres ();
|
||||
#endif
|
||||
|
||||
call_function_static_weak (__libc_dlerror_result_free);
|
||||
|
||||
for (p = symbol_set_first_element (__libc_freeres_ptrs);
|
||||
!symbol_set_end_p (__libc_freeres_ptrs, p); ++p)
|
||||
free (*p);
|
||||
|
@ -16,6 +16,7 @@
|
||||
License along with the GNU C Library; if not, see
|
||||
<https://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <dlfcn/dlerror.h>
|
||||
#include <libc-internal.h>
|
||||
#include <malloc-internal.h>
|
||||
#include <resolv/resolv-internal.h>
|
||||
@ -36,6 +37,7 @@ __libc_thread_freeres (void)
|
||||
#endif
|
||||
call_function_static_weak (__res_thread_freeres);
|
||||
__glibc_tls_internal_free ();
|
||||
call_function_static_weak (__libc_dlerror_result_free);
|
||||
|
||||
/* This should come last because it shuts down malloc for this
|
||||
thread and the other shutdown functions might well call free. */
|
||||
|
@ -668,6 +668,9 @@ struct rtld_global_ro
|
||||
int (*_dl_catch_error) (const char **objname, const char **errstring,
|
||||
bool *mallocedp, void (*operate) (void *),
|
||||
void *args);
|
||||
/* libdl in a secondary namespace must use free from the base
|
||||
namespace. */
|
||||
void (*_dl_error_free) (void *);
|
||||
void *(*_dl_tls_get_addr_soft) (struct link_map *);
|
||||
#ifdef HAVE_DL_DISCOVER_OSVERSION
|
||||
int (*_dl_discover_osversion) (void);
|
||||
@ -823,6 +826,10 @@ void _dl_exception_create (struct dl_exception *, const char *object,
|
||||
__attribute__ ((nonnull (1, 3)));
|
||||
rtld_hidden_proto (_dl_exception_create)
|
||||
|
||||
/* Used internally to implement dlerror message freeing. See
|
||||
include/dlfcn.h and dlfcn/dlerror.c. */
|
||||
void _dl_error_free (void *ptr) attribute_hidden;
|
||||
|
||||
/* Like _dl_exception_create, but create errstring from a format
|
||||
string FMT. Currently, only "%s" and "%%" are supported as format
|
||||
directives. */
|
||||
|
Loading…
Reference in New Issue
Block a user