From: Adhemerval Zanella <adhemerval.zanella@linaro.org>
To: libc-alpha@sourceware.org, Florian Weimer <fweimer@redhat.com>
Subject: Re: [PATCH v3 20/37] dlfcn: dlerror needs to call free from the base namespace [BZ #24773]
Date: Tue, 23 Mar 2021 11:47:55 -0300 [thread overview]
Message-ID: <55d65ab2-235b-12e6-667b-10aa8b4c125f@linaro.org> (raw)
In-Reply-To: <e8706f25e68240a6432a851441502fcdedc0561b.1615914632.git.fweimer@redhat.com>
On 16/03/2021 14:30, Florian Weimer via Libc-alpha wrote:
> 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).
> ---
> dlfcn/Makefile | 3 +-
> dlfcn/Versions | 6 +-
> dlfcn/dlerror.c | 299 ++++++++++++++--------------------
> dlfcn/dlerror.h | 74 +++++++++
> dlfcn/dlfreeres.c | 29 ----
> dlfcn/libc_dlerror_result.c | 39 +++++
> elf/dl-exception.c | 11 ++
> elf/rtld.c | 1 +
> elf/tst-dlmopen-dlerror-mod.c | 29 +++-
> elf/tst-dlmopen-dlerror.c | 22 ++-
> include/dlfcn.h | 2 -
> malloc/set-freeres.c | 10 +-
> malloc/thread-freeres.c | 2 +
> sysdeps/generic/ldsodefs.h | 7 +
> 14 files changed, 307 insertions(+), 227 deletions(-)
> create mode 100644 dlfcn/dlerror.h
> delete mode 100644 dlfcn/dlfreeres.c
> create mode 100644 dlfcn/libc_dlerror_result.c
>
> diff --git a/dlfcn/Makefile b/dlfcn/Makefile
> index d51fd08c33..994a3afee6 100644
> --- a/dlfcn/Makefile
> +++ b/dlfcn/Makefile
> @@ -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
>
Ok.
> diff --git a/dlfcn/Versions b/dlfcn/Versions
> index 1df6925a92..f07cb929aa 100644
> --- a/dlfcn/Versions
> +++ b/dlfcn/Versions
> @@ -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;
> }
> }
Ok.
> diff --git a/dlfcn/dlerror.c b/dlfcn/dlerror.c
> index 947b7c10c6..e0dedbe3a6 100644
> --- a/dlfcn/dlerror.c
> +++ b/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,89 +38,70 @@ 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);
> -
> -
Ok.
> 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;
> - else
> + /* 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 ())
> {
> - /* 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;
> + __libc_dlerror_result = NULL;
> + return (char *) "out of memory";
> }
Ok.
>
> - /* Test whether we already returned the string. */
> - if (result->returned != 0)
> + /* 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. */
> + if (result->returned)
> {
> - /* 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;
> - }
> + __libc_dlerror_result = NULL;
> + dl_action_result_errstring_free (result);
> + free (result);
> + return NULL;
> }
Isn't this essentially '__libc_dlerror_result_free()' ?
> - 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 returned. */
> - result->returned = 1;
> + 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
> + n = __asprintf (&buf, "%s%s%s: %s",
> + result->objname,
> + result->objname[0] == '\0' ? "" : ": ",
> + _(result->errstring),
> + strerror (result->errcode));
> +
> + /* Mark the error as delivered. */
> + result->returned = true;
Shouldn't we handle asprintf error here?
> +
> + 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;
Ok.
> @@ -130,130 +113,94 @@ strong_alias (__dlerror, dlerror)
> int
> _dlerror_run (void (*operate) (void *), void *args)
> {
> - struct dl_action_result *result;
> -
> - /* If we have not yet initialized the buffer do it now. */
> - __libc_once (once, init);
> -
> - /* Get error string and number. */
> - if (static_buf != NULL)
> - result = static_buf;
> - else
> + struct dl_action_result *result = __libc_dlerror_result;
> + if (result != NULL)
> {
> - /* 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)
> + if (result == dl_action_result_malloc_failed ())
> {
> - result = (struct dl_action_result *) calloc (1, 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);
> + /* 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;
> }
> }
>
Ok.
> - 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);
> + const char *objname;
> + const char *errstring;
> + bool malloced;
> + int errcode = GLRO (dl_catch_error) (&objname, &errstring, &malloced,
> + operate, args);
>
> - /* If no error we mark that no error string is available. */
> - result->returned = result->errstring == NULL;
> + /* ELF constructors or destructors may have indirectly altered the
> + value of __libc_dlerror_result, therefore reload it. */
> + result = __libc_dlerror_result;
>
> - return result->errstring != NULL;
> -}
> -
Ok.
> -
> -/* 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)
> + if (errstring == NULL)
> {
> - /* 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
> + /* 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)
> {
> - free ((char *) rec->errstring);
> - rec->errstring = NULL;
> + __libc_dlerror_result = NULL;
> + free (result);
> }
> + return 0;
> }
Ok.
> -}
> -
> -
> -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);
> + else
> + {
> + /* A new error occurred. Check if a result object has to be
> + allocated. */
> + if (result == NULL || result == dl_action_result_malloc_failed ())
> + {
> + /* 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)
> + {
> + /* 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;
>
> - free (mem);
> - __libc_setspecific (key, NULL);
> + return 1;
> + }
> }
>
> # ifdef SHARED
>
Ok.
> -/* 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)
>
Ok.
> diff --git a/dlfcn/dlerror.h b/dlfcn/dlerror.h
> new file mode 100644
> index 0000000000..ae07495139
> --- /dev/null
> +++ b/dlfcn/dlerror.h
> @@ -0,0 +1,74 @@
Missing header? I am never sure if it is required for internal
headers.
> +#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;
> + }
> +}
> +
Ok.
> +static inline struct dl_action_result *
> +dl_action_result_malloc_failed (void)
> +{
> + return (struct dl_action_result *) (intptr_t) -1;
> +}
> +
Why not use a static const variable as suggested by Joseph?
> +/* 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 */
k.
> diff --git a/dlfcn/dlfreeres.c b/dlfcn/dlfreeres.c
> deleted file mode 100644
> index 856b76416d..0000000000
> --- a/dlfcn/dlfreeres.c
> +++ /dev/null
> @@ -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);
> -}
Ok.
> diff --git a/dlfcn/libc_dlerror_result.c b/dlfcn/libc_dlerror_result.c
> new file mode 100644
> index 0000000000..11468937a2
> --- /dev/null
> +++ b/dlfcn/libc_dlerror_result.c
> @@ -0,0 +1,39 @@
> +/* Thread-local variable holding the dlerror result.
> + Copyright (C) 2019 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;
> + }
> +}
Ok.
> diff --git a/elf/dl-exception.c b/elf/dl-exception.c
> index 30adb7d1dc..8eaad418cb 100644
> --- a/elf/dl-exception.c
> +++ b/elf/dl-exception.c
> @@ -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
Ok.
> diff --git a/elf/rtld.c b/elf/rtld.c
> index fd02438936..c2ca4b7ce3 100644
> --- a/elf/rtld.c
> +++ b/elf/rtld.c
> @@ -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
Pl.
> diff --git a/elf/tst-dlmopen-dlerror-mod.c b/elf/tst-dlmopen-dlerror-mod.c
> index dcb94320b4..25188750fb 100644
> --- a/elf/tst-dlmopen-dlerror-mod.c
> +++ b/elf/tst-dlmopen-dlerror-mod.c
> @@ -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);
> }
>
Ok.
> 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);
> }
Ok.
> diff --git a/elf/tst-dlmopen-dlerror.c b/elf/tst-dlmopen-dlerror.c
> index 65638f7f38..d02993847d 100644
> --- a/elf/tst-dlmopen-dlerror.c
> +++ b/elf/tst-dlmopen-dlerror.c
> @@ -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");
> -
> - call_dlsym ();
> - call_dlopen ();
> + void (*call_dlsym) (const char *name) = xdlsym (handle, "call_dlsym");
> + void (*call_dlopen) (const char *name) = xdlsym (handle, "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;
> }
Ok.
> diff --git a/include/dlfcn.h b/include/dlfcn.h
> index a1816e4991..a8d48bdada 100644
> --- a/include/dlfcn.h
> +++ b/include/dlfcn.h
> @@ -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
Ok.
> diff --git a/malloc/set-freeres.c b/malloc/set-freeres.c
> index 817fbea8b8..d404250151 100644
> --- a/malloc/set-freeres.c
> +++ b/malloc/set-freeres.c
> @@ -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
Ok.
> @@ -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)
Ok.
> @@ -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);
Ok.
> diff --git a/malloc/thread-freeres.c b/malloc/thread-freeres.c
> index da76a3dca7..77a204f9fa 100644
> --- a/malloc/thread-freeres.c
> +++ b/malloc/thread-freeres.c
> @@ -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. */
Ok.
> diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
> index b207f229c3..dfc117a445 100644
> --- a/sysdeps/generic/ldsodefs.h
> +++ b/sysdeps/generic/ldsodefs.h
> @@ -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. */
>
Ok.
next prev parent reply other threads:[~2021-03-23 14:47 UTC|newest]
Thread overview: 99+ messages / expand[flat|nested] mbox.gz Atom feed top
2021-03-16 17:27 [PATCH v3 00/37] libpthread removal: NPTL forwarders are gone Florian Weimer
2021-03-16 17:27 ` [PATCH v3 01/37] nptl: Move pthread_mutex_consistent into libc Florian Weimer
2021-03-17 11:36 ` Adhemerval Zanella
2021-03-16 17:27 ` [PATCH v3 02/37] nptl: Move __pthread_cleanup_routine " Florian Weimer
2021-03-16 17:27 ` [PATCH v3 03/37] nptl: Move legacy unwinding implementation " Florian Weimer
2021-03-16 17:27 ` [PATCH v3 04/37] nptl: Move legacy cancelation handling into libc as compat symbols Florian Weimer
2021-03-16 17:27 ` [PATCH v3 05/37] nptl: Remove longjmp, siglongjmp from libpthread Florian Weimer
2021-03-17 11:38 ` Adhemerval Zanella
2021-03-16 17:28 ` [PATCH v3 06/37] x86: Restore compile-time check for shadow stack pointer in longjmp Florian Weimer
2021-03-16 17:28 ` [PATCH v3 07/37] nptl: Move __pthread_cleanup_upto into libc Florian Weimer
2021-03-16 17:28 ` [PATCH v3 08/37] nptl: Move pthread_once and __pthread_once " Florian Weimer
2021-03-17 13:30 ` Adhemerval Zanella
2021-03-17 13:37 ` Adhemerval Zanella
2021-03-17 14:45 ` Florian Weimer
2021-03-17 16:39 ` Adhemerval Zanella
2021-03-17 16:56 ` Florian Weimer
2021-03-17 17:22 ` Adhemerval Zanella
2021-03-17 17:43 ` Florian Weimer
2021-03-17 19:09 ` Adhemerval Zanella
2021-03-16 17:29 ` [PATCH v3 09/37] nptl: Move __pthread_unwind_next " Florian Weimer
2021-03-17 19:42 ` Adhemerval Zanella
2021-03-17 19:54 ` Florian Weimer
2021-03-17 20:16 ` Adhemerval Zanella
2021-03-17 20:33 ` Florian Weimer
2021-03-17 20:44 ` Adhemerval Zanella
2021-03-16 17:29 ` [PATCH v3 10/37] csu: Move calling main out of __libc_start_main_impl Florian Weimer
2021-03-17 20:45 ` Adhemerval Zanella
2021-03-16 17:29 ` [PATCH v3 11/37] nptl: Move internal __nptl_nthreads variable into libc Florian Weimer
2021-03-18 12:42 ` Adhemerval Zanella
2021-03-16 17:29 ` [PATCH v3 12/37] nptl_db: Introduce DB_MAIN_ARRAY_VARIABLE Florian Weimer
2021-03-18 12:43 ` Adhemerval Zanella
2021-03-16 17:29 ` [PATCH v3 13/37] nptl: Move __pthread_keys global variable into libc Florian Weimer
2021-03-18 12:44 ` Adhemerval Zanella
2021-03-16 17:29 ` [PATCH v3 14/37] nptl: Move __nptl_deallocate_tsd " Florian Weimer
2021-03-18 12:46 ` Adhemerval Zanella
2021-03-18 17:16 ` Florian Weimer
2021-03-18 17:54 ` Adhemerval Zanella
2021-03-16 17:29 ` [PATCH v3 15/37] nptl: Move pthread_exit " Florian Weimer
2021-03-18 12:49 ` Adhemerval Zanella
2021-03-16 17:29 ` [PATCH v3 16/37] nptl: Move pthread_setcancelstate " Florian Weimer
2021-03-18 12:52 ` Adhemerval Zanella
2021-03-16 17:29 ` [PATCH v3 17/37] nptl: Move pthread_setcanceltype " Florian Weimer
2021-03-18 12:53 ` Adhemerval Zanella
2021-03-16 17:29 ` [PATCH v3 18/37] nptl: Invoke the set_robust_list system call directly in fork Florian Weimer
2021-03-18 12:54 ` Adhemerval Zanella
2021-03-16 17:30 ` [PATCH v3 19/37] dlfcn: Failures after dlmopen should not terminate process [BZ #24772] Florian Weimer
2021-03-19 19:56 ` Adhemerval Zanella
2021-03-27 16:57 ` Florian Weimer
2021-03-16 17:30 ` [PATCH v3 20/37] dlfcn: dlerror needs to call free from the base namespace [BZ #24773] Florian Weimer
2021-03-23 14:47 ` Adhemerval Zanella [this message]
2021-03-16 17:30 ` [PATCH v3 21/37] Remove pthread_key_create-related internals from libc-lock.h Florian Weimer
2021-03-23 16:39 ` Adhemerval Zanella
2021-03-16 17:30 ` [PATCH v3 22/37] elf: Introduce __tls_init_tp for second-phase TCB initialization Florian Weimer
2021-03-23 18:25 ` Adhemerval Zanella
2021-03-16 17:30 ` [PATCH v3 23/37] nptl: Move part of TCB initialization from libpthread to __tls_init_tp Florian Weimer
2021-03-24 13:56 ` Adhemerval Zanella
2021-03-27 17:19 ` Florian Weimer
2021-03-16 17:30 ` [PATCH v3 24/37] nptl: Move pthread_key_create, __pthread_key_create into libc Florian Weimer
2021-03-24 14:09 ` Adhemerval Zanella
2021-03-24 14:32 ` Florian Weimer
2021-03-24 14:42 ` Adhemerval Zanella
2021-03-24 15:08 ` Florian Weimer
2021-03-24 15:46 ` Adhemerval Zanella
2021-03-16 17:30 ` [PATCH v3 25/37] nptl: Move pthread_getspecific, __pthread_getspecific " Florian Weimer
2021-03-24 14:12 ` Adhemerval Zanella
2021-03-24 14:38 ` Florian Weimer
2021-03-24 14:43 ` Adhemerval Zanella
2021-03-16 17:30 ` [PATCH v3 26/37] nptl: Move pthread_setspecific, __pthread_setspecific " Florian Weimer
2021-03-24 14:26 ` Adhemerval Zanella
2021-03-16 17:30 ` [PATCH v3 27/37] nptl: Move pthread_key_delete " Florian Weimer
2021-03-24 14:45 ` Adhemerval Zanella
2021-03-16 17:31 ` [PATCH v3 28/37] nptl: Move rwlock functions with forwarders " Florian Weimer
2021-03-25 19:52 ` Adhemerval Zanella
2021-03-27 21:41 ` Florian Weimer
2021-03-16 17:31 ` [PATCH v3 29/37] nptl: Move the internal thread priority protection symbols " Florian Weimer
2021-03-25 20:21 ` Adhemerval Zanella
2021-03-16 17:31 ` [PATCH v3 30/37] pthread: Introduce __pthread_early_init Florian Weimer
2021-03-25 20:22 ` Adhemerval Zanella
2021-03-16 17:31 ` [PATCH v3 31/37] nptl: Move internal symbol __mutex_aconf into libc Florian Weimer
2021-03-25 20:24 ` Adhemerval Zanella
2021-03-16 17:31 ` [PATCH v3 32/37] nptl: pthread_mutex_lock, pthread_mutex_unock single-threaded optimization Florian Weimer
2021-03-26 18:00 ` Adhemerval Zanella
2021-03-16 17:31 ` [PATCH v3 33/37] x86: Remove low-level lock optimization Florian Weimer
2021-03-25 20:30 ` Adhemerval Zanella
2021-03-16 17:31 ` [PATCH v3 34/37] nptl: Move core mutex functions into libc Florian Weimer
2021-03-25 20:46 ` Adhemerval Zanella
2021-03-16 17:31 ` [PATCH v3 35/37] nptl: Move core condition variable " Florian Weimer
2021-03-26 17:14 ` Adhemerval Zanella
2021-03-16 17:31 ` [PATCH v3 36/37] nptl: Move setxid broadcast implementation " Florian Weimer
2021-03-26 18:15 ` Adhemerval Zanella
2021-04-06 18:41 ` Florian Weimer
2021-04-06 18:54 ` Adhemerval Zanella
2021-04-06 19:23 ` Florian Weimer
2021-04-06 19:40 ` Adhemerval Zanella
2021-03-16 17:31 ` [PATCH v3 37/37] nptl: Remove remnants of the libc/libpthread forwarder interface Florian Weimer
2021-03-26 18:19 ` Adhemerval Zanella
2021-03-18 22:06 ` [PATCH v3 00/37] libpthread removal: NPTL forwarders are gone Florian Weimer
2021-03-26 18:25 ` Adhemerval Zanella
2021-03-31 10:18 ` Florian Weimer
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=55d65ab2-235b-12e6-667b-10aa8b4c125f@linaro.org \
--to=adhemerval.zanella@linaro.org \
--cc=fweimer@redhat.com \
--cc=libc-alpha@sourceware.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).