give libc access to its own malloc even if public malloc is interposed

allowing the application to replace malloc (since commit
c9f415d7ea) has brought multiple
headaches where it's used from various critical sections in libc
components. for example:

- the thread-local message buffers allocated for dlerror can't be
  freed at thread exit time because application code would then run in
  the context of a non-existant thread. this was handled in commit
  aa5a9d15e0 by queuing them for free
  later.

- the dynamic linker has to be careful not to pass memory allocated at
  early startup time (necessarily using its own malloc) to realloc or
  free after redoing relocations with the application and all
  libraries present. bugs in this area were fixed several times, at
  least in commits 0c5c8f5da6 and
  2f1f51ae7b and possibly others.

- by calling the allocator from contexts where libc-internal locks are
  held, we impose undocumented requirements on alternate malloc
  implementations not to call into any libc function that might
  attempt to take these locks; if they do, deadlock results.

- work to make fork of a multithreaded parent give the child an
  unrestricted execution environment is blocked by lock order issues
  as long as the application-provided allocator can be called with
  libc-internal locks held.

these problems are all fixed by giving libc internals access to the
original, non-replaced allocator, for use where needed. it can't be
used everywhere, as some interfaces like str[n]dup, open_[w]memstream,
getline/getdelim, etc. are required to provide the called memory
obtained as if by (the public) malloc. and there are a number of libc
interfaces that are "pure library" code, not part of some internal
singleton, and where using the application's choice of malloc
implementation is preferable -- things like glob, regex, etc.

one might expect there to be significant cost to static-linked
programs, pulling in two malloc implementations, one of them
mostly-unused, if malloc is replaced. however, in almost all of the
places where malloc is used internally, care has been taken already
not to pull in realloc/free (i.e. to link with just the bump
allocator). this size optimization carries over automatically.

the newly-exposed internal allocator functions are obtained by
renaming the actual definitions, then adding new wrappers around them
with the public names. technically __libc_realloc and __libc_free
could be aliases rather than needing a layer of wrapper, but this
would almost surely break certain instrumentation (valgrind) and the
size and performance difference is negligible. __libc_calloc needs to
be handled specially since calloc is designed to work with either the
internal or the replaced malloc.

as a bonus, this change also eliminates the longstanding ugly
dependency of the static bump allocator on order of object files in
libc.a, by making it so there's only one definition of the malloc
function and having it in the same source file as the bump allocator.
This commit is contained in:
Rich Felker 2020-11-11 00:22:34 -05:00
parent c1e5d243b7
commit 8d37958d58
7 changed files with 43 additions and 1 deletions

View File

@ -9,4 +9,10 @@ hidden int __mkostemps(char *, int, int);
hidden int __ptsname_r(int, char *, size_t);
hidden char *__randname(char *);
hidden void *__libc_malloc(size_t);
hidden void *__libc_malloc_impl(size_t);
hidden void *__libc_calloc(size_t, size_t);
hidden void *__libc_realloc(void *, size_t);
hidden void __libc_free(void *);
#endif

6
src/malloc/free.c Normal file
View File

@ -0,0 +1,6 @@
#include <stdlib.h>
void free(void *p)
{
return __libc_free(p);
}

4
src/malloc/libc_calloc.c Normal file
View File

@ -0,0 +1,4 @@
#define calloc __libc_calloc
#define malloc __libc_malloc
#include "calloc.c"

View File

@ -100,4 +100,16 @@ static void *__simple_malloc(size_t n)
return p;
}
weak_alias(__simple_malloc, malloc);
weak_alias(__simple_malloc, __libc_malloc_impl);
void *__libc_malloc(size_t n)
{
return __libc_malloc_impl(n);
}
static void *default_malloc(size_t n)
{
return __libc_malloc_impl(n);
}
weak_alias(default_malloc, malloc);

View File

@ -20,6 +20,10 @@
#define is_allzero __malloc_allzerop
#define dump_heap __dump_heap
#define malloc __libc_malloc_impl
#define realloc __libc_realloc
#define free __libc_free
#if USE_REAL_ASSERT
#include <assert.h>
#else

View File

@ -10,6 +10,10 @@
#include "pthread_impl.h"
#include "malloc_impl.h"
#define malloc __libc_malloc
#define realloc __libc_realloc
#define free __libc_free
#if defined(__GNUC__) && defined(__PIC__)
#define inline inline __attribute__((always_inline))
#endif

6
src/malloc/realloc.c Normal file
View File

@ -0,0 +1,6 @@
#include <stdlib.h>
void *realloc(void *p, size_t n)
{
return __libc_realloc(p, n);
}