From patchwork Sat Jun 30 02:57:28 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Carlos O'Donell X-Patchwork-Id: 28168 Received: (qmail 45541 invoked by alias); 30 Jun 2018 02:57:46 -0000 Mailing-List: contact libc-alpha-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: libc-alpha-owner@sourceware.org Delivered-To: mailing list libc-alpha@sourceware.org Received: (qmail 45531 invoked by uid 89); 30 Jun 2018 02:57:45 -0000 Authentication-Results: sourceware.org; auth=none X-Spam-SWARE-Status: No, score=-26.9 required=5.0 tests=BAYES_00, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, RCVD_IN_DNSWL_NONE, TIME_LIMIT_EXCEEDED autolearn=unavailable version=3.3.2 spammy=vital, Automatic, occasional, HX-Received:sk:y10-v6m X-HELO: mail-qt0-f169.google.com Return-Path: To: DJ Delorie , Florian Weimer , GNU C Library , "Joseph S. Myers" From: Carlos O'Donell Subject: [PATCH v2] malloc: Remove malloc hooks (Bug 23328) Message-ID: Date: Fri, 29 Jun 2018 22:57:28 -0400 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Thunderbird/52.8.0 MIME-Version: 1.0 v2 - I worked with DJ to remove/finalize __morecore, __after_morecore_hook, and __default_morecore, along with fixing a bug with the mcheck support (an application requesting mcheck via MALLOC_CHECK_=3 or tunables or mallopt that *didn't* preload libmalloc-extras.so could abort with memory corruption). - Turned all the hooks into compat symbols so no new ABI will have them, but used GLIBC_PRIVATE symbols to keep libmalloc-extras.so working. - Fixed up the manual. - Added a NEWS entry. From dd6867aca22e0b0b02e94bd30e2cfddb48a1aade Mon Sep 17 00:00:00 2001 From: Carlos O'Donell Date: Thu, 21 Jun 2018 16:18:36 -0400 Subject: [PATCH] malloc: Remove malloc hooks (Bug 23328) This commit removes the following malloc functionality: __free_hook, __malloc_hook, __realloc_hook, __memalign_hook, __morecore, __default_morecore, __after_morecore_hook. The hooks are removed from the public header, and changed to compat symbols. The hooks themselves are removed from the glibc malloc implementation. The manual is updated to remove all references to the hooks. Lastly the hooks are kept, even for new ABIs, as GLIBC_PRIVATE symbols in order to allow the dependent APIs to continue to work e.g. mtrace via the preloaded libmalloc-extras.so. The morecore-related functionality is all changed to compat symbols and is no longer used, but overall code cleanup and refactoring of __default_morecore is left for later. For applications that require the hooks the functionality is moved entirely to a new shared library: libmalloc-extras.so. Applications should preload libmalloc-extras.so to get back the lost functionality. Static applications are not supported, but continue to operate with APIs doing nothing or returning errors depending on their semantics. The manual is updated to reference the new libmalloc-extras.so. The APIs that use the hooks are left e.g. mtrace, muntrace, mprobe, mcheck, mcheck_pedantic, mcheck_check_all, mallwatch, tr_break. These APIs could one day be implemented differently, and libmalloc-extras.so might no longer be needed. Removing the hooks should improve the security (no more writable function pointers), performance (no checking of the function pointers), and maintainability of glibc's malloc (avoids complexity of the hooks and calling back into malloc from them). For users who require these hooks, or the services behind them e.g. mtrace, the hooks are re-implemented in a separate libmalloc-extras.so that can be preloaded via LD_PRELOAD. This new libmalloc-extras.so interposes the malloc API and implements the hook functionality. The functionality which is no longer supported is that provided by __morecore, and __after_morecore_hook. The morecore related hooking is entirely removed, and __default_morecore is always called with no interposition possible (now named __glibc_morecore). Applications using the morecore hooks will find their hooks are no longer called and will need to switch to a malloc interposer. In the case of tests like tst-leaks1-static-mem it was not possible to test the new hooks because they are only usable by dynamically loaded binaries. The interposable library is not made available for static linkage. The APIs that have not been deprecated, like mcheck, can still be called by static applications, but they will not do anything useful, and that is within the bounds of the API description. There are some cases where not doing anything useful will result in a crash. Take for example the case of calling mcheck () to look for consistency errors. Previously they would be caught by the registered function, but in the new glibc the application actually aborts because the checker does nothing unless libmalloc-extras.so is preloaded. All of these cases are already undefined behaviour. Wethere the registered function is called or the application aborts is a QoI issue at that point. These changes could be backported to older branches by dropping the compat symbol and public header changes. Regression tested on x86_64 with no failures. Example new/finalized symbol set for x86_64: New libmalloc-extras.so interface: __glibc_malloc_check_request@@GLIBC_PRIVATE __glibc_malloc_check_enable@@GLIBC_PRIVATE __glibc_malloc_hook@@GLIBC_PRIVATE __glibc_realloc_hook@@GLIBC_PRIVATE __glibc_memalign_hook@@GLIBC_PRIVATE __glibc_free_hook@@GLIBC_PRIVATE Note: The hooks are exported GLIBC_PRIVATE such that new machines being implemented can drop the deprected symbols, and still have libmalloc-extras.so continue to work using the private interface. Finalized libc.so.6 interfaces: __malloc_hook@GLIBC_2.2.5 (alias to __glibc_* version) __memalign_hook@GLIBC_2.2.5 (likewise) __realloc_hook@GLIBC_2.2.5 (likewise) __free_hook@GLIBC_2.2.5 (likewise) __morecore@GLIBC_2.2.5 (unused) __default_morecore@GLIBC_2.2.5 (default) __after_morecore_hook@GLIBC_2.2.5 (unused) Signed-off-by: DJ Delorie Signed-off-by: Carlos O'Donell --- ChangeLog | 135 +++++ Makeconfig | 2 +- NEWS | 26 + catgets/Makefile | 3 +- elf/Makefile | 17 +- iconvdata/Makefile | 3 +- include/stdlib.h | 3 - intl/tst-gettext.sh | 1 + libio/Makefile | 9 +- localedata/Makefile | 3 +- malloc/Makefile | 28 +- malloc/Versions | 8 + malloc/hooks.c | 74 ++- malloc/malloc-extras.c | 792 +++++++++++++++++++++++++++++ malloc/malloc-hooks.h | 8 + malloc/malloc-internal.h | 4 + malloc/malloc.c | 111 ++-- malloc/malloc.h | 25 - malloc/mcheck.c | 364 +------------ malloc/morecore.c | 6 +- malloc/mtrace.c | 281 +--------- malloc/tst-malloc-usable-static-tunables.c | 1 - malloc/tst-malloc-usable-static.c | 1 - malloc/tst-malloc-weak-usable-static.c | 1 + malloc/tst-malloc-weak-usable.c | 60 +++ malloc/tst-mtrace.sh | 1 + manual/memory.texi | 239 +-------- misc/Makefile | 6 +- nptl/Makefile | 6 +- posix/Makefile | 39 +- posix/tst-vfork3.c | 6 + resolv/Makefile | 9 +- stdio-common/Makefile | 6 +- 33 files changed, 1260 insertions(+), 1018 deletions(-) create mode 100644 malloc/malloc-extras.c delete mode 100644 malloc/tst-malloc-usable-static-tunables.c delete mode 100644 malloc/tst-malloc-usable-static.c create mode 100644 malloc/tst-malloc-weak-usable-static.c create mode 100644 malloc/tst-malloc-weak-usable.c diff --git a/ChangeLog b/ChangeLog index 3371a9b670..b55a14a386 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,138 @@ +2018-06-29 DJ Delorie + Carlos O'Donell + + [BZ #23328] + * Makeconfig (built-modules): Add libmalloc-extras. + * NEWS: Describe deprecation of hooks. + * catgets/Makefile (tst-catgets-ENV): Preload libmalloc-extras.so. + * elf/Makefile (tests-special): Remove tst-leaks1-static-mem.out. + (noload-ENV): Preload libmalloc-extras.so. + ($(objpfx)tst-leaks1-static-mem.out): Remove. + (tst-leaks1-ENV): Likewise. + * iconvdata/Makefile (tst-loading-ENV): Likewise. + * include/stdlib.h: Remove __default_morecore prototype. + * intl/tst-gettext.sh: Preload libmalloc-extras.so. + * libio/Makefile (test-fmemopen-ENV): Likewise. + (tst-fopenloc-ENV): Likewise. + (tst-bz22415-ENV): Likewise. + * localedata/Makefile (tst-leaks-ENV): Likewise. + * malloc/Makefile (tests): Add tst-malloc-weak-usable. + (tests-static): Add tst-malloc-weak-usable-static. + Remove tst-malloc-usable-static. + [ifneq (no,$(have-tunables))] (tests-static): Remove + tst-malloc-usable-tunables-static. + (extra-libs): Add libmalloc-extras. + (libmalloc-extras-routines): Define. + (LDLIBS-libmalloc-extras.so): Define. + (LDFLAGS-malloc-extras.so): Define. + ($(objpfx)libmalloc-extras.so): Define. + (tst-mtrace-ENV): Preload libmalloc-extras.so. + (tst-malloc-usable-ENV): Likewise. + (tst-malloc-usable-tunables-ENV): Likewise. + (tst-malloc-weak-usable-ENV): Define. + (tst-malloc-weak-usable-static-ENV): Define. + (tst-dynarray-ENV): Likewise. + (tst-dynarray-fail-ENV): Likewise. + * malloc/Versions (libc.GLIBC_PRIVATE): Add __glibc_free_hook, + __glibc_malloc_hook, __glibc_realloc_hook, __glibc_memalign_hook, + __glibc_malloc_check_request, __glibC_malloc_check_enable. + * malloc/hooks.c: Define declrations for malloc_check, + free_check, realloc_check, and memalign_check. + (malloc_hook_ini): Call interposable API and use local hooks. + (realloc_hook_ini): Likewise. + (memalign_hook_ini): Likewise. + (__glibc_malloc_check_request): New global data. + (__malloc_check_init): Install but don't enable hooks. + (__glibc_malloc_check_enable): New function. + (mem2mem_check): Adjust comment. + (mem2chunk_check): Adjust comment. + (malloc_set_state): Use local hooks. + * malloc/malloc-extras.c: New file. + * malloc/malloc-hooks.h: Add prototypes for __glibc_free_hook, + __glibc_malloc_hook, __glibc_realloc_hook, __glibc_memalign_hook, + __glibc_malloc_check_request, __glibc_malloc_check_enable. + * malloc/malloc-internal.h: Add prototypes for __malloc_check_init, + __glibc_morecore. + * malloc/malloc.c: Remove prototypes for __default_morecore, + mem2mem_check, top_check, malloc_check, free_check, realloc_check, and + memalign_check. + (MORECORE): Call __glibc_morecore directly. + (__morecore): Declare as compat symbol conditional on + SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_28). + (__free_hook): Rename to... + (__glibc_free_hook): ...this. And declare as compat symbol + __free_hook conditional on + SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_28). + (__malloc_hook): Rename to... + (__glibc_malloc_hook): ...this. And declare as compat symbol + __malloc_hook conditional on + SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_28). + (__realloc_hook): Rename to... + (__glibc_realloc_hook): ...this. And declare as compat symbol + __glibc_realloc_hook conditional on + SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_28). + (__memalign_hook): Rename to... + (__glibc_memalign_hook): ...this. And declare as compat symbol + __glibc_memalign_hook conditional on + SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_28). + (__after_morecore_hook): Declare as compat symbol conditional on + SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_28). + (sysmalloc): Do not call __after_morecore_hook. + (systrim): Likewise. + (__libc_malloc): Call ptmalloc_init() and remove hook calls. + (__libc_free): Likewise. + (__libc_realloc): Likewise. + (__libc_calloc): Likewise. + (_mid_memaligin): Don't call __memalign_hook. + * malloc/malloc.h: Remove public hook declarations for __free_hook, + __malloc_hook, __realloc_hook, __memalign_hook, and + __after_morecore_hook. Remove delcaration of __default_morecore, + and __morecore. + * malloc/mcheck.c: Remove most code, leaving an empty implementation + of the APIs for mcheck_check_all, mcheck, mcheck_pedantic, and mprobe. + * malloc/morecore.c (__default_morecore): Rename to... + (__glibc_morecore): ...this. Define as compat symbol + __default_morecore conditional on + SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_28). + * malloc/mtrace.c: Remove most code, leaving an empty implementation + of the APIs for mallwatch, tr_break, mtrace, and muntrace. + * malloc/tst-malloc-usable-static-tunables.c: Remove file. + * malloc/tst-malloc-usable-static.c: Remove file. + * malloc/tst-malloc-weak-usable-static.c: New file. + * malloc/tst-malloc-weak-usable.c: New file. + * malloc/tst-mtrace.sh: Preload libmalloc-extras.so. + * manual/memory.texi (Summary of Malloc): Remove "Hooks for Malloc". + (Hooks for Malloc): Remove. + (mcheck): Remove @mtasuconst{:malloc_hooks} safety not. + (mprobe): Likewise. + (mtrace): Likewise. + (muntrace): Likewise. + (Heap Consistency Checking): Document need for libmalloc-extra.so. + (Allocation Debugging): Likewise. + * misc/Makefile (tst-error1-ENV): Likewise. + (tst-allocate_once-ENV): Likewise. + * nptl/Makefile (tst-atfork2-ENV): Likewise. + (tst-stack3-ENV): Likewise. + * posix/Makefile (tst-fnmatch-ENV): Likewise. + (bug-regex2-ENV): Likewise. + (bug-regex14-ENV): Likewise. + (bug-regex21-ENV): Likewise. + (bug-regex31-ENV): Likewise. + (bug-regex36-ENV): Likewise. + (tst-vfork3-ENV): Likewise. + (tst-rxspencer-no-utf8-ENV): Likewise. + (tst-pcre-ENV): Likewise. + (tst-boost-ENV): Likewise. + (bug-ga2-ENV): Likewise. + (bug-glob2-ENV): Likewise. + (tst-glob-tilde-ENV): Likewise. + * posix/tst-vfork3.c (do_prepare): Unset LD_PRELOAD. + * resolv/Makefile (tst-leaks-ENV): Preload libmalloc-extras.so. + (tst-leaks2-ENV): Likewise. + (tst-resolv-res_ninit-ENV): Likewise. + * stdio-common/Makefile (tst-printf-bz18872-ENV): Likewise. + (tst-vfprintf-width-prec-ENV): Likewise. + 2018-06-29 DJ Delorie Carlos O'Donell diff --git a/Makeconfig b/Makeconfig index 608ffe648c..ece6f13198 100644 --- a/Makeconfig +++ b/Makeconfig @@ -929,7 +929,7 @@ libio-include = -I$(..)libio built-modules = iconvprogs iconvdata ldconfig lddlibc4 libmemusage \ libSegFault libpcprofile librpcsvc locale-programs \ memusagestat nonlib nscd extramodules libnldbl libsupport \ - testsuite + testsuite libmalloc-extras in-module = $(subst -,_,$(firstword $(libof-$(basename $(@F))) \ $(libof-$( and <_G_config.h> are no longer installed. Software that was using either header should be updated to use standard interfaces instead. diff --git a/catgets/Makefile b/catgets/Makefile index 36f6a226c3..941e5cb4ea 100644 --- a/catgets/Makefile +++ b/catgets/Makefile @@ -56,7 +56,8 @@ generated += tst-catgets.mtrace tst-catgets-mem.out generated-dirs += de -tst-catgets-ENV = NLSPATH="$(objpfx)%l/%N.cat" LANG=de MALLOC_TRACE=$(objpfx)tst-catgets.mtrace +tst-catgets-ENV = NLSPATH="$(objpfx)%l/%N.cat" LANG=de MALLOC_TRACE=$(objpfx)tst-catgets.mtrace \ + LD_PRELOAD=$(objpfx)../malloc/libmalloc-extras.so ifeq ($(run-built-tests),yes) # This test just checks whether the program produces any error or not. diff --git a/elf/Makefile b/elf/Makefile index 41cc3681be..d61bbecab8 100644 --- a/elf/Makefile +++ b/elf/Makefile @@ -205,7 +205,7 @@ endif tests += $(tests-execstack-$(have-z-execstack)) ifeq ($(run-built-tests),yes) tests-special += $(objpfx)tst-leaks1-mem.out \ - $(objpfx)tst-leaks1-static-mem.out $(objpfx)noload-mem.out \ + $(objpfx)noload-mem.out \ $(objpfx)tst-ldconfig-X.out endif tlsmod17a-suffixes = 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @@ -812,7 +812,8 @@ $(objpfx)noload.out: $(objpfx)testobj5.so $(objpfx)noload-mem.out: $(objpfx)noload.out $(common-objpfx)malloc/mtrace $(objpfx)noload.mtrace > $@; \ $(evaluate-test) -noload-ENV = MALLOC_TRACE=$(objpfx)noload.mtrace +noload-ENV = MALLOC_TRACE=$(objpfx)noload.mtrace \ + LD_PRELOAD=$(objpfx)../malloc/libmalloc-extras.so LDFLAGS-nodelete = -rdynamic LDFLAGS-nodelmod1.so = -Wl,--enable-new-dtags,-z,nodelete @@ -1227,11 +1228,15 @@ $(objpfx)tst-leaks1-mem.out: $(objpfx)tst-leaks1.out $(evaluate-test) $(objpfx)tst-leaks1-static: $(common-objpfx)dlfcn/libdl.a -$(objpfx)tst-leaks1-static-mem.out: $(objpfx)tst-leaks1-static.out - $(common-objpfx)malloc/mtrace $(objpfx)tst-leaks1-static.mtrace > $@; \ - $(evaluate-test) -tst-leaks1-ENV = MALLOC_TRACE=$(objpfx)tst-leaks1.mtrace +tst-leaks1-ENV = MALLOC_TRACE=$(objpfx)tst-leaks1.mtrace \ + LD_PRELOAD=$(objpfx)../malloc/libmalloc-extras.so +# Static applications no longer support tracing. All of the hooks used +# for tracing have been removed. However, we can still test that the +# application runs using the existing API calls without crashing. +# Note that there is no tst-leaks1-static-mem.out target which runs +# mtrace in this case because no *.mtrace file would be generated, again +# we are just testing that the application itself doesn't crash. tst-leaks1-static-ENV = MALLOC_TRACE=$(objpfx)tst-leaks1-static.mtrace $(objpfx)tst-addr1: $(libdl) diff --git a/iconvdata/Makefile b/iconvdata/Makefile index 06e161d9b8..3930890903 100644 --- a/iconvdata/Makefile +++ b/iconvdata/Makefile @@ -292,7 +292,8 @@ cpp-srcs-left := $(modules) $(generated-modules) $(libJIS-routines) \ lib := iconvdata include $(patsubst %,$(..)libof-iterator.mk,$(cpp-srcs-left)) -tst-loading-ENV = MALLOC_TRACE=$(objpfx)tst-loading.mtrace +tst-loading-ENV = MALLOC_TRACE=$(objpfx)tst-loading.mtrace \ + LD_PRELOAD=$(objpfx)../malloc/libmalloc-extras.so $(objpfx)mtrace-tst-loading.out: $(objpfx)tst-loading.out $(common-objpfx)malloc/mtrace $(objpfx)tst-loading.mtrace > $@; \ $(evaluate-test) diff --git a/include/stdlib.h b/include/stdlib.h index 114e12d255..bd3dcf4950 100644 --- a/include/stdlib.h +++ b/include/stdlib.h @@ -285,9 +285,6 @@ libc_hidden_proto (__qfcvt_r) # define MB_CUR_MAX (_NL_CURRENT_WORD (LC_CTYPE, _NL_CTYPE_MB_CUR_MAX)) # endif -extern void *__default_morecore (ptrdiff_t) __THROW; -libc_hidden_proto (__default_morecore) - struct abort_msg_s { unsigned int size; diff --git a/intl/tst-gettext.sh b/intl/tst-gettext.sh index 0c65583149..c49f61586c 100755 --- a/intl/tst-gettext.sh +++ b/intl/tst-gettext.sh @@ -50,6 +50,7 @@ msgfmt -o ${objpfx}domaindir/existing-locale/LC_TIME/existing-time-domain.mo \ ${test_program_prefix_before_env} \ ${run_program_env} \ MALLOC_TRACE=$malloc_trace \ +LD_PRELOAD=${common_objpfx}/malloc/libmalloc-extras.so \ LOCPATH=${objpfx}localedir:${common_objpfx}localedata \ ${test_program_prefix_after_env} \ ${objpfx}tst-gettext > ${objpfx}tst-gettext.out ${objpfx}domaindir diff --git a/libio/Makefile b/libio/Makefile index 64d283e512..6781f6f8b2 100644 --- a/libio/Makefile +++ b/libio/Makefile @@ -154,9 +154,12 @@ CFLAGS-tst_putwc.c += -DOBJPFX=\"$(objpfx)\" tst_wprintf2-ARGS = "Some Text" -test-fmemopen-ENV = MALLOC_TRACE=$(objpfx)test-fmemopen.mtrace -tst-fopenloc-ENV = MALLOC_TRACE=$(objpfx)tst-fopenloc.mtrace -tst-bz22415-ENV = MALLOC_TRACE=$(objpfx)tst-bz22415.mtrace +test-fmemopen-ENV = MALLOC_TRACE=$(objpfx)test-fmemopen.mtrace \ + LD_PRELOAD=$(objpfx)../malloc/libmalloc-extras.so +tst-fopenloc-ENV = MALLOC_TRACE=$(objpfx)tst-fopenloc.mtrace \ + LD_PRELOAD=$(objpfx)../malloc/libmalloc-extras.so +tst-bz22415-ENV = MALLOC_TRACE=$(objpfx)tst-bz22415.mtrace \ + LD_PRELOAD=$(objpfx)../malloc/libmalloc-extras.so generated += test-fmemopen.mtrace test-fmemopen.check generated += tst-fopenloc.mtrace tst-fopenloc.check diff --git a/localedata/Makefile b/localedata/Makefile index d51064adec..71e87c88d6 100644 --- a/localedata/Makefile +++ b/localedata/Makefile @@ -385,7 +385,8 @@ $(INSTALL-SUPPORTED-LOCALES): install-locales-dir tst-setlocale-ENV = LC_ALL=ja_JP.EUC-JP tst-wctype-ENV = LC_ALL=ja_JP.EUC-JP -tst-leaks-ENV = MALLOC_TRACE=$(objpfx)tst-leaks.mtrace +tst-leaks-ENV = MALLOC_TRACE=$(objpfx)tst-leaks.mtrace \ + LD_PRELOAD=$(objpfx)/../malloc/libmalloc-extras.so $(objpfx)mtrace-tst-leaks.out: $(objpfx)tst-leaks.out $(common-objpfx)malloc/mtrace $(objpfx)tst-leaks.mtrace > $@; \ $(evaluate-test) diff --git a/malloc/Makefile b/malloc/Makefile index 7d54bad866..98ca0c56fc 100644 --- a/malloc/Makefile +++ b/malloc/Makefile @@ -38,11 +38,12 @@ tests := mallocbug tst-malloc tst-valloc tst-calloc tst-obstack \ tst-malloc_info \ tst-malloc-too-large \ tst-malloc-stats-cancellation \ + tst-malloc-weak-usable tests-static := \ tst-interpose-static-nothread \ tst-interpose-static-thread \ - tst-malloc-usable-static \ + tst-malloc-weak-usable-static tests-internal := tst-mallocstate tst-scratch_buffer @@ -54,7 +55,6 @@ tests-internal += \ ifneq (no,$(have-tunables)) tests += tst-malloc-usable-tunables -tests-static += tst-malloc-usable-static-tunables endif tests += $(tests-static) @@ -78,7 +78,7 @@ install-lib := libmcheck.a non-lib.a := libmcheck.a # Additional library. -extra-libs = libmemusage +extra-libs = libmemusage libmalloc-extras extra-libs-others = $(extra-libs) # Helper objects for some tests. @@ -143,6 +143,11 @@ $(objpfx)memusagestat.o: sysincludes = # nothing endif endif +libmalloc-extras-routines = malloc-extras +LDLIBS-libmalloc-extras.so = $(shared-thread-library) +LDFLAGS-malloc-extras.so = -Wl,-z,nodelete +$(objpfx)libmalloc-extras.so: $(libdl) + # Another goal which can be used to override the configure decision. .PHONY: do-memusagestat do-memusagestat: $(objpfx)memusagestat @@ -189,11 +194,14 @@ endif endif endif +tst-mtrace-ENV = LD_PRELOAD=$(objpfx)libmalloc-extras.so tst-mcheck-ENV = MALLOC_CHECK_=3 -tst-malloc-usable-ENV = MALLOC_CHECK_=3 -tst-malloc-usable-static-ENV = $(tst-malloc-usable-ENV) -tst-malloc-usable-tunables-ENV = GLIBC_TUNABLES=glibc.malloc.check=3 -tst-malloc-usable-static-tunables-ENV = $(tst-malloc-usable-tunables-ENV) +tst-malloc-usable-ENV = MALLOC_CHECK_=3 LD_PRELOAD=$(objpfx)libmalloc-extras.so +tst-malloc-usable-tunables-ENV = GLIBC_TUNABLES=glibc.malloc.check=3 \ + LD_PRELOAD=$(objpfx)libmalloc-extras.so +# Do not preload libmalloc-extras.so for tst-malloc-weak-usable* tests. +tst-malloc-weak-usable-ENV = MALLOC_CHECK_=3 +tst-malloc-weak-usable-static-ENV = MALLOC_CHECK_=3 ifeq ($(experimental-malloc),yes) CPPFLAGS-malloc.c += -DUSE_TCACHE=1 @@ -239,12 +247,14 @@ $(objpfx)tst-interpose-static-nothread: $(objpfx)tst-interpose-aux-nothread.o $(objpfx)tst-interpose-static-thread: \ $(objpfx)tst-interpose-aux-thread.o $(static-thread-library) -tst-dynarray-ENV = MALLOC_TRACE=$(objpfx)tst-dynarray.mtrace +tst-dynarray-ENV = MALLOC_TRACE=$(objpfx)tst-dynarray.mtrace \ + LD_PRELOAD=$(objpfx)../malloc/libmalloc-extras.so $(objpfx)tst-dynarray-mem.out: $(objpfx)tst-dynarray.out $(common-objpfx)malloc/mtrace $(objpfx)tst-dynarray.mtrace > $@; \ $(evaluate-test) -tst-dynarray-fail-ENV = MALLOC_TRACE=$(objpfx)tst-dynarray-fail.mtrace +tst-dynarray-fail-ENV = MALLOC_TRACE=$(objpfx)tst-dynarray-fail.mtrace \ + LD_PRELOAD=$(objpfx)../malloc/libmalloc-extras.so $(objpfx)tst-dynarray-fail-mem.out: $(objpfx)tst-dynarray-fail.out $(common-objpfx)malloc/mtrace $(objpfx)tst-dynarray-fail.mtrace > $@; \ $(evaluate-test) diff --git a/malloc/Versions b/malloc/Versions index 2357cff3da..4a7967e790 100644 --- a/malloc/Versions +++ b/malloc/Versions @@ -92,5 +92,13 @@ libc { __libc_alloc_buffer_copy_bytes; __libc_alloc_buffer_copy_string; __libc_alloc_buffer_create_failure; + + # Implementation of libmalloc-extras.so. + __glibc_free_hook; + __glibc_malloc_hook; + __glibc_realloc_hook; + __glibc_memalign_hook; + __glibc_malloc_check_request; + __glibc_malloc_check_enable; } } diff --git a/malloc/hooks.c b/malloc/hooks.c index ae7305b036..d854c0bbba 100644 --- a/malloc/hooks.c +++ b/malloc/hooks.c @@ -21,46 +21,82 @@ corrupt pointer is detected: do nothing (0), print an error message (1), or call abort() (2). */ +/* Forward declarations of the check functions for use by the hooks. */ +static void *malloc_check(size_t sz, const void *caller); +static void free_check(void *mem, const void *caller); +static void *realloc_check(void *oldmem, size_t bytes, + const void *caller); +static void *memalign_check(size_t alignment, size_t bytes, + const void *caller); + /* Hooks for debugging versions. The initial hooks just call the initialization routine, then do the normal work. */ static void * malloc_hook_ini (size_t sz, const void *caller) { - __malloc_hook = NULL; + __glibc_malloc_hook = NULL; ptmalloc_init (); - return __libc_malloc (sz); + /* We can't call __libc_malloc because it doesn't support hooks; we + have to re-call the interposed malloc which does, if it's present + (which it should be, else we wouldn't get here in the first + place. */ + return malloc (sz); } static void * realloc_hook_ini (void *ptr, size_t sz, const void *caller) { - __malloc_hook = NULL; - __realloc_hook = NULL; + __glibc_malloc_hook = NULL; + __glibc_realloc_hook = NULL; ptmalloc_init (); - return __libc_realloc (ptr, sz); + return realloc (ptr, sz); } static void * memalign_hook_ini (size_t alignment, size_t sz, const void *caller) { - __memalign_hook = NULL; + __glibc_memalign_hook = NULL; ptmalloc_init (); - return __libc_memalign (alignment, sz); + return memalign (alignment, sz); } /* Whether we are using malloc checking. */ static int using_malloc_checking; -/* Activate a standard set of debugging hooks. */ +/* In __malloc_check_init we do not enable checking, but we install + the hooks. The hooks are no longer used unless libmalloc-extras.so + is preloaded. Therefore we leave the hooks disabled until + the library tells us it has enabled them. The value is a simple + protocol, it starts 0, we install the check hooks and set it to 1, + the interposing implementation calls __glibc_malloc_check_enable + when ready, and we set using_malloc_checking to 1 and + __glibc_malloc_check_request to zero. That completes the transition + between libc.so and libmalloc-extras.so. */ +int __glibc_malloc_check_request; + +/* Install a standard set of debugging hooks. */ void __malloc_check_init (void) +{ + /* Don't enable checking yet... */ + using_malloc_checking = 0; + /* Request that libmalloc-extras.so enable checking once it has + been loaded. */ + __glibc_malloc_check_request = 1; + __glibc_malloc_hook = malloc_check; + __glibc_free_hook = free_check; + __glibc_realloc_hook = realloc_check; + __glibc_memalign_hook = memalign_check; +} + +/* Enable the installed debugging hooks. + Called from libmalloc-extras.so. */ +void +__glibc_malloc_check_enable (void) { using_malloc_checking = 1; - __malloc_hook = malloc_check; - __free_hook = free_check; - __realloc_hook = realloc_check; - __memalign_hook = memalign_check; + __glibc_malloc_check_request = 0; } /* A simple, standard set of debugging hooks. Overhead is `only' one @@ -109,8 +145,7 @@ malloc_check_get_size (mchunkptr p) } /* Instrument a chunk with overrun detector byte(s) and convert it - into a user pointer with requested size req_sz. */ - + into a user pointer with requested size req_sz. */ static void * mem2mem_check (void *ptr, size_t req_sz) { @@ -142,8 +177,7 @@ mem2mem_check (void *ptr, size_t req_sz) } /* Convert a pointer to be free()d or realloc()ed to a valid chunk - pointer. If the provided pointer is not valid, return NULL. */ - + pointer. If the provided pointer is not valid, return NULL. */ static mchunkptr mem2chunk_check (void *mem, unsigned char **magic_p) { @@ -453,10 +487,10 @@ malloc_set_state (void *msptr) cannot be more than one thread when we reach this point. */ /* Disable the malloc hooks (and malloc checking). */ - __malloc_hook = NULL; - __realloc_hook = NULL; - __free_hook = NULL; - __memalign_hook = NULL; + __glibc_malloc_hook = NULL; + __glibc_realloc_hook = NULL; + __glibc_free_hook = NULL; + __glibc_memalign_hook = NULL; using_malloc_checking = 0; /* Patch the dumped heap. We no longer try to integrate into the diff --git a/malloc/malloc-extras.c b/malloc/malloc-extras.c new file mode 100644 index 0000000000..38735d2a56 --- /dev/null +++ b/malloc/malloc-extras.c @@ -0,0 +1,792 @@ +/* Provide malloc hook functionality outside of libc. + Copyright (C) 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 +#include +#include + +#include + +#include "malloc-internal.h" +#include "malloc-hooks.h" +#include "mcheck.h" + +static void *(*next_malloc) (size_t) = NULL; +static void (*next_free) (void *) = NULL; +static void *(*next_realloc) (void *, size_t) = NULL; +static void *(*next_calloc) (size_t, size_t) = NULL; +static void *(*next_memalign) (size_t, size_t) = NULL; +static size_t (*next_malloc_usable_size) (void *) = NULL; + +#define TRACE_BUFFER_SIZE 512 + +static FILE *mallstream; +static const char mallenv[] = "MALLOC_TRACE"; +static char *malloc_trace_buffer; + +__libc_lock_define_initialized (static, lock); + +#define TRACE_LOCK(caller, info) \ + caller = RETURN_ADDRESS (0); \ + dladdr (caller, &info); \ + __libc_lock_lock (lock); + +#define TRACE_LOCK_N(caller, info) \ + dladdr (caller, &info); \ + __libc_lock_lock (lock); + +#define TRACE_UNLOCK() \ + __libc_lock_unlock (lock); + + +/*======================================================================*/ +/* mtrace support. */ + +static void +tr_where (const void *caller, Dl_info *info) +{ + if (caller != NULL) + { + if (info != NULL) + { + fprintf (mallstream, "@ %s%s", + info->dli_fname ? : "", info->dli_fname ? ":" : ""); + + if (info->dli_sname != NULL) + { + if (caller > (const void *) info->dli_saddr) + fprintf (mallstream, "(%s+0x%lx)", info->dli_sname, + (long unsigned)(caller - (const void *) info->dli_saddr)); + else + fprintf (mallstream, "(%s-0x%lx)", info->dli_sname, + (long unsigned)((const void *) info->dli_saddr - caller)); + } + + fprintf (mallstream, "[%p] ", caller); + } + else + fprintf (mallstream, "@ [%p] ", caller); + } +} + + +/*======================================================================*/ +/* mcheck support. */ + +/* Function to call when something awful happens. */ +static void (*abortfunc) (enum mcheck_status); + +static int __malloc_extras_initialized = 0; + +/* Arbitrary magical numbers. */ +#define MAGICWORD 0xfedabeeb +#define MAGICFREE 0xd8675309 +#define MAGICBYTE ((char) 0xd7) +#define MALLOCFLOOD ((char) 0x93) +#define FREEFLOOD ((char) 0x95) + +struct hdr +{ + size_t size; /* Exact size requested by user. */ + unsigned long int magic; /* Magic number to check header integrity. */ + struct hdr *prev; + struct hdr *next; + void *block; /* Real block allocated, for memalign. */ + unsigned long int magic2; /* Extra, keeps us doubleword aligned. */ +}; + +/* This is the beginning of the list of all memory blocks allocated. + It is only constructed if the pedantic testing is requested. */ +static struct hdr *root; + +static int mcheck_used; + +/* Nonzero if pedentic checking of all blocks is requested. */ +static int pedantic; + +static void +flood (void *ptr, int val, size_t size) +{ + char *cp = ptr; + while (size--) + *cp++ = val; +} + +static enum mcheck_status +checkhdr (const struct hdr *hdr) +{ + enum mcheck_status status; + + if (!mcheck_used) + /* Maybe the mcheck used is disabled? This happens when we find + an error and report it. */ + return MCHECK_OK; + + switch (hdr->magic ^ ((uintptr_t) hdr->prev + (uintptr_t) hdr->next)) + { + default: + status = MCHECK_HEAD; + break; + case MAGICFREE: + status = MCHECK_FREE; + break; + case MAGICWORD: + if (((char *) &hdr[1])[hdr->size] != MAGICBYTE) + status = MCHECK_TAIL; + else if ((hdr->magic2 ^ (uintptr_t) hdr->block) != MAGICWORD) + status = MCHECK_HEAD; + else + status = MCHECK_OK; + break; + } + if (status != MCHECK_OK) + { + mcheck_used = 0; + (*abortfunc) (status); + mcheck_used = 1; + } + return status; +} + +void +mcheck_check_all (void) +{ + /* Walk through all the active blocks and test whether they were tampered + with. */ + struct hdr *runp = root; + + /* Temporarily turn off the checks. */ + pedantic = 0; + + while (runp != NULL) + { + (void) checkhdr (runp); + + runp = runp->next; + } + + /* Turn checks on again. */ + pedantic = 1; +} + +static void +unlink_blk (struct hdr *ptr) +{ + if (ptr->next != NULL) + { + ptr->next->prev = ptr->prev; + ptr->next->magic = MAGICWORD ^ ((uintptr_t) ptr->next->prev + + (uintptr_t) ptr->next->next); + } + if (ptr->prev != NULL) + { + ptr->prev->next = ptr->next; + ptr->prev->magic = MAGICWORD ^ ((uintptr_t) ptr->prev->prev + + (uintptr_t) ptr->prev->next); + } + else + root = ptr->next; +} + +static void +link_blk (struct hdr *hdr) +{ + hdr->prev = NULL; + hdr->next = root; + root = hdr; + hdr->magic = MAGICWORD ^ (uintptr_t) hdr->next; + + /* And the next block. */ + if (hdr->next != NULL) + { + hdr->next->prev = hdr; + hdr->next->magic = MAGICWORD ^ ((uintptr_t) hdr + + (uintptr_t) hdr->next->next); + } +} + +__attribute__ ((noreturn)) +static void +mabort (enum mcheck_status status) +{ + const char *msg; + switch (status) + { + case MCHECK_OK: + msg = _ ("memory is consistent, library is buggy\n"); + break; + case MCHECK_HEAD: + msg = _ ("memory clobbered before allocated block\n"); + break; + case MCHECK_TAIL: + msg = _ ("memory clobbered past end of allocated block\n"); + break; + case MCHECK_FREE: + msg = _ ("block freed twice\n"); + break; + default: + msg = _ ("bogus mcheck_status, library is buggy\n"); + break; + } + fprintf (stderr, "mcheck: %s", msg); + fflush (stderr); + abort (); +} + +/* Memory barrier so that GCC does not optimize out the argument. */ +#define malloc_opt_barrier(x) \ + ({ __typeof (x) __x = x; __asm ("" : "+m" (__x)); __x; }) + +int +mcheck (void (*func) (enum mcheck_status)) +{ + abortfunc = (func != NULL) ? func : &mabort; + setbuf (stdout, NULL); + + /* These hooks may not be safely inserted if malloc is already in use. */ + if (__malloc_extras_initialized <= 0 && !mcheck_used) + { + /* We call malloc () once here to ensure it is initialized. */ + void *p = malloc (0); + p = realloc (p, 16); + void *p2 = memalign (4, 0); + /* GCC might optimize out the malloc/free pair without a barrier. */ + p = malloc_opt_barrier (p); + p2 = malloc_opt_barrier (p2); + free (p); + free (p2); + + mcheck_used = 1; + } + + return mcheck_used ? 0 : -1; +} + +int +mcheck_pedantic (void (*func) (enum mcheck_status)) +{ + int res = mcheck (func); + if (res == 0) + pedantic = 1; + return res; +} + +enum mcheck_status +mprobe (void *ptr) +{ + return mcheck_used ? checkhdr (((struct hdr *) ptr) - 1) : MCHECK_DISABLED; +} + +/* For these, a round/align of zero means "don't". */ + +#define cfa() __builtin_return_address (0) + +#define MCHECK_PRE(size, align_ptr_to) \ + if (mcheck_used) \ + { \ + if (size > ~((size_t) 0) - (sizeof (struct hdr) + 1) - align_ptr_to) \ + { \ + __set_errno (ENOMEM); \ + return NULL; \ + } \ + size += align_ptr_to; \ + size += sizeof (struct hdr) + 1; \ + } + + +#define MCHECK_POST_NOFLOOD(ptr, original_size, align_to) \ + if (mcheck_used) \ + { \ + void *block = (void *) ptr; \ + struct hdr *hdr = (struct hdr *) block; \ + ptr = (void *) (hdr + 1); \ + if (align_to) \ + { \ + ptr = PTR_ALIGN_UP (ptr, align_to); \ + hdr = (struct hdr *)ptr - 1; \ + } \ + hdr->size = original_size; \ + link_blk (hdr); \ + hdr->block = (void *) block; \ + hdr->magic2 = (uintptr_t) block ^ MAGICWORD; \ + ((char *) &hdr[1])[original_size] = MAGICBYTE; \ + } + + +#define MCHECK_POST(ptr, original_size, align_to) \ + if (mcheck_used) \ + { \ + MCHECK_POST_NOFLOOD (ptr, original_size, align_to); \ + flood (ptr, MALLOCFLOOD, original_size); \ + } + +#define MCHECK_PREFREE(ptr) \ + if (mcheck_used && ptr != NULL) \ + { \ + struct hdr *hdr = ((struct hdr *) ptr) - 1; \ + checkhdr (hdr); \ + hdr->magic = MAGICFREE; \ + hdr->magic2 = MAGICFREE; \ + unlink_blk (hdr); \ + hdr->prev = hdr->next = NULL; \ + flood (ptr, FREEFLOOD, hdr->size); \ + ptr = hdr->block; \ + } + +/*======================================================================*/ +/* mallwatch, hooks */ + +/* Address to breakpoint on accesses to... */ +void *mallwatch = (void *) (-1); + +/* This function is called when the block being alloc'd, realloc'd, or + freed has an address matching the variable "mallwatch". In a debugger, + set "mallwatch" to the address of interest, then put a breakpoint on + tr_break. */ + +extern void tr_break (void) __THROW; +libc_hidden_proto (tr_break) +void __attribute__ ((noinline)) +tr_break (void) +{ + /* Never optimize it away either. */ + asm (""); +} + +static int recursing = 0; + +/* Call a hook function FUNC, and if recusion is detected return + with value ERV. In addition we check for + __glibc_malloc_check_requested to see if the runtime is waiting + to know if the hooks are active and if malloc_usable_size can + use the more accurate checking sizes for chunks. */ +#define HOOK(func, erv) \ + if (next_##func == NULL) \ + { \ + int r; \ + r = atomic_load_acquire (&recursing); \ + if (r) \ + return erv; \ + __typeof__ (next_##func) tmp; \ + atomic_add (&recursing, 1); \ + tmp = dlsym (RTLD_NEXT, #func); \ + atomic_store_relaxed (&next_##func, tmp); \ + if (tmp == NULL) \ + abort (); \ + atomic_add (&recursing, -1); \ + atomic_store_relaxed (&__malloc_extras_initialized, 1); \ + if (__glibc_malloc_check_request) \ + __glibc_malloc_check_enable (); \ + } + +void * +malloc (size_t sz) +{ + void *rv; + size_t original_size = sz; + void *caller; + Dl_info info; + + void *(*hook) (size_t, const void *) + = atomic_forced_read (__glibc_malloc_hook); + if (__builtin_expect (hook != NULL, 0)) + return (*hook) (sz, RETURN_ADDRESS (0)); + + HOOK (malloc, NULL); + + MCHECK_PRE (sz, 0); + + rv = next_malloc (sz); + + MCHECK_POST (rv, original_size, 0); + + if (mallstream) + { + TRACE_LOCK (caller, info); + tr_where (caller, &info); + /* We could be printing a NULL here; that's OK. */ + fprintf (mallstream, "+ %p %#lx\n", rv, (unsigned long int) original_size); + TRACE_UNLOCK (); + } + + if (rv == mallwatch) + tr_break (); + + return rv; +} + +void +free (void *ptr) +{ + void *caller; + Dl_info info; + + if (ptr == NULL) + return; + + void (*hook) (void *, const void *) + = atomic_forced_read (__glibc_free_hook); + if (__builtin_expect (hook != NULL, 0)) + { + (*hook)(ptr, RETURN_ADDRESS (0)); + return; + } + + HOOK (free, ); + + if (mallstream && ptr) + { + TRACE_LOCK (caller, info); + tr_where (caller, &info); + /* We could be printing a NULL here; that's OK. */ + fprintf (mallstream, "- %p\n", ptr); + TRACE_UNLOCK (); + } + + if (ptr == mallwatch) + tr_break (); + + MCHECK_PREFREE (ptr); + + next_free (ptr); + + return; +} + +void * +realloc (void *ptr, size_t sz) +{ + void *rv; + size_t original_size = sz; + void *caller; + Dl_info info; + + void *(*hook) (void *, size_t, const void *) = + atomic_forced_read (__glibc_realloc_hook); + if (__builtin_expect (hook != NULL, 0)) + return (*hook)(ptr, sz, RETURN_ADDRESS (0)); + + HOOK (realloc, NULL); + + if (ptr == mallwatch) + tr_break (); + + MCHECK_PREFREE (ptr); + MCHECK_PRE (sz, 0); + + rv = next_realloc (ptr, sz); + + MCHECK_POST (ptr, original_size, 0); + + if (mallstream) + { + TRACE_LOCK (caller, info); + tr_where (caller, &info); + + if (rv == NULL) + { + if (original_size != 0) + /* Failed realloc. */ + fprintf (mallstream, "! %p %#lx\n", ptr, (unsigned long int) original_size); + else + fprintf (mallstream, "- %p\n", ptr); + } + else if (ptr == NULL) + fprintf (mallstream, "+ %p %#lx\n", rv, (unsigned long int) original_size); + else + { + fprintf (mallstream, "< %p\n", ptr); + tr_where (caller, &info); + fprintf (mallstream, "> %p %#lx\n", rv, (unsigned long int) original_size); + } + + TRACE_UNLOCK (); + } + + if (rv == mallwatch) + tr_break (); + + return rv; +} + +void * +calloc (size_t sz1, size_t sz2) +{ + void *rv; + size_t original_size; + size_t sz = sz1 * sz2; + void *caller; + Dl_info info; + +#define HALF_INTERNAL_SIZE_T \ + (((size_t) 1) << (8 * sizeof (size_t) / 2)) + if (__builtin_expect ((sz1 | sz2) >= HALF_INTERNAL_SIZE_T, 0)) + { + if (sz1 != 0 && sz / sz1 != sz2) + { + __set_errno (ENOMEM); + return 0; + } + } + + original_size = sz; + + void *(*hook) (size_t, const void *) = + atomic_forced_read (__glibc_malloc_hook); + if (__builtin_expect (hook != NULL, 0)) + { + rv = (*hook)(sz, RETURN_ADDRESS (0)); + if (rv == 0) + return 0; + + memset (rv, 0, sz); + + if (rv == mallwatch) + tr_break (); + + return rv; + } + + HOOK (calloc, NULL); + + MCHECK_PRE (sz, 0); + + rv = next_calloc (sz, 1); + + MCHECK_POST_NOFLOOD (rv, sz, 0); + + if (mallstream) + { + TRACE_LOCK (caller, info); + tr_where (caller, &info); + /* We could be printing a NULL here; that's OK. */ + fprintf (mallstream, "+ %p %#lx\n", rv, (unsigned long int) original_size); + TRACE_UNLOCK (); + } + + if (rv == mallwatch) + tr_break (); + + return rv; +} + +static void * +memalign_common (size_t sz1, size_t sz2, void *caller) +{ + void *rv; + size_t original_size = sz2; + Dl_info info; + + void *(*hook) (size_t, size_t, const void *) = + atomic_forced_read (__glibc_memalign_hook); + if (__builtin_expect (hook != NULL, 0)) + return (*hook)(sz1, sz2, RETURN_ADDRESS (0)); + + HOOK (memalign, NULL); + + MCHECK_PRE (sz2, sz1); + + rv = next_memalign (sz1, sz2); + + MCHECK_POST (rv, original_size, sz1); + + if (mallstream) + { + TRACE_LOCK_N (caller, info); + tr_where (caller, &info); + /* We could be printing a NULL here; that's OK. */ + fprintf (mallstream, "+ %p %#lx\n", rv, (unsigned long int) original_size); + TRACE_UNLOCK (); + } + + if (rv == mallwatch) + tr_break (); + + return rv; +} + +void * +memalign (size_t sz1, size_t sz2) +{ + return memalign_common (sz1, sz2, RETURN_ADDRESS (0)); +} + +int +posix_memalign (void **memptr, size_t alignment, size_t size) +{ + void *rvptr; + + /* Test whether the SIZE argument is valid. It must be a power of + two multiple of sizeof (void *). */ + if (alignment % sizeof (void *) != 0 + || !powerof2 (alignment / sizeof (void *)) + || alignment == 0) + return EINVAL; + + rvptr = memalign_common (alignment, size, RETURN_ADDRESS (0)); + + if (rvptr) + { + *memptr = rvptr; + return 0; + } + + return ENOMEM; +} + +static size_t remembered_pagesize = 0; + +static inline size_t +pagesize (void) +{ + size_t rv = atomic_load_relaxed (&remembered_pagesize); + if (remembered_pagesize == 0) + { + rv = sysconf (_SC_PAGESIZE); + atomic_store_relaxed (&remembered_pagesize, rv); + } + return rv; +} + +void * +valloc (size_t sz) +{ + return memalign_common (sysconf (_SC_PAGESIZE), sz, RETURN_ADDRESS (0)); +} + +void * +pvalloc (size_t sz) +{ + size_t rounded_bytes = ALIGN_UP (sz, pagesize ()); + return memalign_common (pagesize (), rounded_bytes, RETURN_ADDRESS (0)); +} + +size_t +malloc_usable_size (void *ptr) +{ + size_t rv; + + HOOK (malloc_usable_size, 0); + + if (!mcheck_used) + rv = next_malloc_usable_size (ptr); + else + { + struct hdr *hdr = (struct hdr *) ptr; + -- hdr; + rv = hdr->size; + } + + return rv; +} + +/*======================================================================*/ + +/* This function gets called to make sure all memory the library + allocates get freed and so does not irritate the user when studying + the mtrace output. */ +static void +release_libc_mem (void) +{ + /* Only call the free function if we still are running in mtrace mode. */ + if (mallstream != NULL) + __libc_freeres (); +} + +void +mtrace (void) +{ + char *mallfile; + static int added_atexit_handler = 0; + + /* Don't panic if we're called more than once. */ + if (mallstream != NULL) + { + fprintf (mallstream, "= Restart?\n"); + return; + } + + mallfile = getenv (mallenv); + if (mallfile != NULL || mallwatch != NULL) + { + char *mtb = malloc (TRACE_BUFFER_SIZE); + if (mtb == NULL) + return; + + mallstream = fopen (mallfile != NULL ? mallfile : "/dev/null", "wce"); + if (mallstream != NULL) + { + /* Be sure it doesn't malloc its buffer! */ + malloc_trace_buffer = mtb; + setvbuf (mallstream, malloc_trace_buffer, _IOFBF, TRACE_BUFFER_SIZE); + fprintf (mallstream, "= Start\n"); + + if (!added_atexit_handler) + { + added_atexit_handler = 1; + atexit ((void (*)(void))release_libc_mem); + } + + } + else + free (mtb); + } +} + +void +muntrace (void) +{ + if (mallstream == NULL) + return; + + /* Do the reverse of what done in mtrace: first reset the hooks and + MALLSTREAM, and only after that write the trailer and close the + file. */ + FILE *f = mallstream; + mallstream = NULL; + + fprintf (f, "= End\n"); + fclose (f); +} + +/*======================================================================*/ +/* user interface */ + +void __attribute__ ((constructor)) +mtrace_extras_ctor (void) +{ + /* Enable checking if it was requested early. */ + if (__glibc_malloc_check_request) + __glibc_malloc_check_enable (); +} + +void __attribute__ ((destructor)) +mtrace_extras_dtor (void) +{ + if (mallstream) + { + __libc_freeres (); + fprintf (mallstream, "= Unload?\n"); + } +} diff --git a/malloc/malloc-hooks.h b/malloc/malloc-hooks.h index f09d2318f4..4abb9c2824 100644 --- a/malloc/malloc-hooks.h +++ b/malloc/malloc-hooks.h @@ -20,5 +20,13 @@ #define _MALLOC_HOOKS_H void (*__malloc_initialize_hook) (void); +extern void (* __glibc_free_hook) (void *__ptr, const void *); +extern void *(* __glibc_malloc_hook)(size_t __size, const void *); +extern void *(* __glibc_realloc_hook)(void *__ptr, size_t __size, const void *); +extern void *(* __glibc_memalign_hook)(size_t __alignment, size_t __size, const void *); + +/* Private APIs for libmalloc-extras.so interaction with hooks. */ +extern int __glibc_malloc_check_request; +void __glibc_malloc_check_enable (void); #endif /* _MALLOC_HOOKS_H */ diff --git a/malloc/malloc-internal.h b/malloc/malloc-internal.h index 9cee0fb2d7..5e2e610cd3 100644 --- a/malloc/malloc-internal.h +++ b/malloc/malloc-internal.h @@ -94,4 +94,8 @@ check_mul_overflow_size_t (size_t left, size_t right, size_t *result) #endif } +void __malloc_check_init (void) __THROW attribute_hidden; + +void *__glibc_morecore (ptrdiff_t increment) attribute_hidden; + #endif /* _MALLOC_INTERNAL_H */ diff --git a/malloc/malloc.c b/malloc/malloc.c index e247c77b7d..b642971e4e 100644 --- a/malloc/malloc.c +++ b/malloc/malloc.c @@ -357,11 +357,12 @@ __malloc_assert (const char *assertion, const char *file, unsigned int line, /* Definition for getting more memory from the OS. */ -#define MORECORE (*__morecore) +#define MORECORE (__glibc_morecore) #define MORECORE_FAILURE 0 -void * __default_morecore (ptrdiff_t); -void *(*__morecore)(ptrdiff_t) = __default_morecore; - +#if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_28) +void *(*__morecore)(ptrdiff_t) = __glibc_morecore; +compat_symbol (libc, __morecore, __morecore, GLIBC_2_0); +#endif #include @@ -998,20 +999,11 @@ static void* _mid_memalign(size_t, size_t, void *); static void malloc_printerr(const char *str) __attribute__ ((noreturn)); -static void* mem2mem_check(void *p, size_t sz); -static void top_check(void); static void munmap_chunk(mchunkptr p); #if HAVE_MREMAP static mchunkptr mremap_chunk(mchunkptr p, size_t new_size); #endif -static void* malloc_check(size_t sz, const void *caller); -static void free_check(void* mem, const void *caller); -static void* realloc_check(void* oldmem, size_t bytes, - const void *caller); -static void* memalign_check(size_t alignment, size_t bytes, - const void *caller); - /* ------------------ MMAP support ------------------ */ @@ -1831,6 +1823,13 @@ static void malloc_consolidate (mstate); # define weak_variable weak_function #endif +#if HAVE_MALLOC_INIT_HOOK +void weak_variable (*__malloc_initialize_hook) (void) = NULL; +compat_symbol (libc, __malloc_initialize_hook, + __malloc_initialize_hook, GLIBC_2_0); +#endif + + /* Forward declarations. */ static void *malloc_hook_ini (size_t sz, const void *caller) __THROW; @@ -1839,24 +1838,33 @@ static void *realloc_hook_ini (void *ptr, size_t sz, static void *memalign_hook_ini (size_t alignment, size_t sz, const void *caller) __THROW; -#if HAVE_MALLOC_INIT_HOOK -void weak_variable (*__malloc_initialize_hook) (void) = NULL; -compat_symbol (libc, __malloc_initialize_hook, - __malloc_initialize_hook, GLIBC_2_0); -#endif -void weak_variable (*__free_hook) (void *__ptr, +void weak_variable (*__glibc_free_hook) (void *__ptr, const void *) = NULL; -void *weak_variable (*__malloc_hook) +void *weak_variable (*__glibc_malloc_hook) (size_t __size, const void *) = malloc_hook_ini; -void *weak_variable (*__realloc_hook) +void *weak_variable (*__glibc_realloc_hook) (void *__ptr, size_t __size, const void *) = realloc_hook_ini; -void *weak_variable (*__memalign_hook) +void *weak_variable (*__glibc_memalign_hook) (size_t __alignment, size_t __size, const void *) = memalign_hook_ini; + +#if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_28) void weak_variable (*__after_morecore_hook) (void) = NULL; +/* When building the shared library we include compat symbols for the + hooks. When building a static application we don't, but we also + don't define the hooks in any public header, so static applications + should not compile. */ +compat_symbol (libc, __glibc_free_hook, __free_hook, GLIBC_2_0); +compat_symbol (libc, __glibc_malloc_hook, __malloc_hook, GLIBC_2_0); +compat_symbol (libc, __glibc_realloc_hook, __realloc_hook, GLIBC_2_0); +compat_symbol (libc, __glibc_memalign_hook, __memalign_hook, GLIBC_2_0); +compat_symbol (libc, __after_morecore_hook, __after_morecore_hook, GLIBC_2_0); +#endif + + /* This function is called from the arena shutdown hook, to free the thread cache (if it exists). */ static void tcache_thread_shutdown (void); @@ -2474,14 +2482,7 @@ sysmalloc (INTERNAL_SIZE_T nb, mstate av) LIBC_PROBE (memory_sbrk_more, 2, brk, size); } - if (brk != (char *) (MORECORE_FAILURE)) - { - /* Call the `morecore' hook if necessary. */ - void (*hook) (void) = atomic_forced_read (__after_morecore_hook); - if (__builtin_expect (hook != NULL, 0)) - (*hook)(); - } - else + if (brk == (char *) (MORECORE_FAILURE)) { /* If have mmap, try using it as a backup when MORECORE fails or @@ -2618,13 +2619,6 @@ sysmalloc (INTERNAL_SIZE_T nb, mstate av) correction = 0; snd_brk = (char *) (MORECORE (0)); } - else - { - /* Call the `morecore' hook if necessary. */ - void (*hook) (void) = atomic_forced_read (__after_morecore_hook); - if (__builtin_expect (hook != NULL, 0)) - (*hook)(); - } } /* handle non-contiguous cases */ @@ -2782,10 +2776,6 @@ systrim (size_t pad, mstate av) */ MORECORE (-extra); - /* Call the `morecore' hook if necessary. */ - void (*hook) (void) = atomic_forced_read (__after_morecore_hook); - if (__builtin_expect (hook != NULL, 0)) - (*hook)(); new_brk = (char *) (MORECORE (0)); LIBC_PROBE (memory_sbrk_less, 2, new_brk, extra); @@ -3013,10 +3003,8 @@ __libc_malloc (size_t bytes) mstate ar_ptr; void *victim; - void *(*hook) (size_t, const void *) - = atomic_forced_read (__malloc_hook); - if (__builtin_expect (hook != NULL, 0)) - return (*hook)(bytes, RETURN_ADDRESS (0)); + if (__malloc_initialized < 0) + ptmalloc_init (); #if USE_TCACHE /* int_free also calls request2size, be careful to not pad twice. */ size_t tbytes; @@ -3071,13 +3059,8 @@ __libc_free (void *mem) mstate ar_ptr; mchunkptr p; /* chunk corresponding to mem */ - void (*hook) (void *, const void *) - = atomic_forced_read (__free_hook); - if (__builtin_expect (hook != NULL, 0)) - { - (*hook)(mem, RETURN_ADDRESS (0)); - return; - } + if (__malloc_initialized < 0) + ptmalloc_init (); if (mem == 0) /* free(0) has no effect */ return; @@ -3117,10 +3100,8 @@ __libc_realloc (void *oldmem, size_t bytes) void *newp; /* chunk to return */ - void *(*hook) (void *, size_t, const void *) = - atomic_forced_read (__realloc_hook); - if (__builtin_expect (hook != NULL, 0)) - return (*hook)(oldmem, bytes, RETURN_ADDRESS (0)); + if (__malloc_initialized < 0) + ptmalloc_init (); #if REALLOC_ZERO_BYTES_FREES if (bytes == 0 && oldmem != NULL) @@ -3246,11 +3227,6 @@ _mid_memalign (size_t alignment, size_t bytes, void *address) mstate ar_ptr; void *p; - void *(*hook) (size_t, size_t, const void *) = - atomic_forced_read (__memalign_hook); - if (__builtin_expect (hook != NULL, 0)) - return (*hook)(alignment, bytes, address); - /* If we need less alignment than we give anyway, just relay to malloc. */ if (alignment <= MALLOC_ALIGNMENT) return __libc_malloc (bytes); @@ -3369,17 +3345,8 @@ __libc_calloc (size_t n, size_t elem_size) } } - void *(*hook) (size_t, const void *) = - atomic_forced_read (__malloc_hook); - if (__builtin_expect (hook != NULL, 0)) - { - sz = bytes; - mem = (*hook)(sz, RETURN_ADDRESS (0)); - if (mem == 0) - return 0; - - return memset (mem, 0, sz); - } + if (__malloc_initialized < 0) + ptmalloc_init (); sz = bytes; diff --git a/malloc/malloc.h b/malloc/malloc.h index 3e7c447be1..b8234cf90b 100644 --- a/malloc/malloc.h +++ b/malloc/malloc.h @@ -71,14 +71,6 @@ extern void *valloc (size_t __size) __THROW __attribute_malloc__ __wur; __size to nearest pagesize. */ extern void *pvalloc (size_t __size) __THROW __attribute_malloc__ __wur; -/* Underlying allocation function; successive calls should return - contiguous pieces of memory. */ -extern void *(*__morecore) (ptrdiff_t __size); - -/* Default value of `__morecore'. */ -extern void *__default_morecore (ptrdiff_t __size) -__THROW __attribute_malloc__; - /* SVID2/XPG mallinfo structure */ struct mallinfo @@ -139,23 +131,6 @@ extern void malloc_stats (void) __THROW; /* Output information about state of allocator to stream FP. */ extern int malloc_info (int __options, FILE *__fp) __THROW; -/* Hooks for debugging and user-defined versions. */ -extern void (*__MALLOC_HOOK_VOLATILE __free_hook) (void *__ptr, - const void *) -__MALLOC_DEPRECATED; -extern void *(*__MALLOC_HOOK_VOLATILE __malloc_hook)(size_t __size, - const void *) -__MALLOC_DEPRECATED; -extern void *(*__MALLOC_HOOK_VOLATILE __realloc_hook)(void *__ptr, - size_t __size, - const void *) -__MALLOC_DEPRECATED; -extern void *(*__MALLOC_HOOK_VOLATILE __memalign_hook)(size_t __alignment, - size_t __size, - const void *) -__MALLOC_DEPRECATED; -extern void (*__MALLOC_HOOK_VOLATILE __after_morecore_hook) (void); - /* Activate a standard set of debugging hooks. */ extern void __malloc_check_init (void) __THROW __MALLOC_DEPRECATED; diff --git a/malloc/mcheck.c b/malloc/mcheck.c index dc04a6391a..a0006c560a 100644 --- a/malloc/mcheck.c +++ b/malloc/mcheck.c @@ -27,374 +27,19 @@ # include #endif -/* Old hook values. */ -static void (*old_free_hook)(void *ptr, const void *); -static void *(*old_malloc_hook) (size_t size, const void *); -static void *(*old_memalign_hook) (size_t alignment, size_t size, - const void *); -static void *(*old_realloc_hook) (void *ptr, size_t size, - const void *); - -/* Function to call when something awful happens. */ -static void (*abortfunc) (enum mcheck_status); - -/* Arbitrary magical numbers. */ -#define MAGICWORD 0xfedabeeb -#define MAGICFREE 0xd8675309 -#define MAGICBYTE ((char) 0xd7) -#define MALLOCFLOOD ((char) 0x93) -#define FREEFLOOD ((char) 0x95) - -struct hdr -{ - size_t size; /* Exact size requested by user. */ - unsigned long int magic; /* Magic number to check header integrity. */ - struct hdr *prev; - struct hdr *next; - void *block; /* Real block allocated, for memalign. */ - unsigned long int magic2; /* Extra, keeps us doubleword aligned. */ -}; - -/* This is the beginning of the list of all memory blocks allocated. - It is only constructed if the pedantic testing is requested. */ -static struct hdr *root; - -static int mcheck_used; - -/* Nonzero if pedentic checking of all blocks is requested. */ -static int pedantic; - -#if defined _LIBC || defined STDC_HEADERS || defined USG -# include -# define flood memset -#else -static void flood (void *, int, size_t); -static void -flood (void *ptr, int val, size_t size) -{ - char *cp = ptr; - while (size--) - *cp++ = val; -} -#endif - -static enum mcheck_status -checkhdr (const struct hdr *hdr) -{ - enum mcheck_status status; - - if (!mcheck_used) - /* Maybe the mcheck used is disabled? This happens when we find - an error and report it. */ - return MCHECK_OK; - - switch (hdr->magic ^ ((uintptr_t) hdr->prev + (uintptr_t) hdr->next)) - { - default: - status = MCHECK_HEAD; - break; - case MAGICFREE: - status = MCHECK_FREE; - break; - case MAGICWORD: - if (((char *) &hdr[1])[hdr->size] != MAGICBYTE) - status = MCHECK_TAIL; - else if ((hdr->magic2 ^ (uintptr_t) hdr->block) != MAGICWORD) - status = MCHECK_HEAD; - else - status = MCHECK_OK; - break; - } - if (status != MCHECK_OK) - { - mcheck_used = 0; - (*abortfunc) (status); - mcheck_used = 1; - } - return status; -} - void mcheck_check_all (void) { - /* Walk through all the active blocks and test whether they were tampered - with. */ - struct hdr *runp = root; - - /* Temporarily turn off the checks. */ - pedantic = 0; - - while (runp != NULL) - { - (void) checkhdr (runp); - - runp = runp->next; - } - - /* Turn checks on again. */ - pedantic = 1; } #ifdef _LIBC libc_hidden_def (mcheck_check_all) #endif -static void -unlink_blk (struct hdr *ptr) -{ - if (ptr->next != NULL) - { - ptr->next->prev = ptr->prev; - ptr->next->magic = MAGICWORD ^ ((uintptr_t) ptr->next->prev - + (uintptr_t) ptr->next->next); - } - if (ptr->prev != NULL) - { - ptr->prev->next = ptr->next; - ptr->prev->magic = MAGICWORD ^ ((uintptr_t) ptr->prev->prev - + (uintptr_t) ptr->prev->next); - } - else - root = ptr->next; -} - -static void -link_blk (struct hdr *hdr) -{ - hdr->prev = NULL; - hdr->next = root; - root = hdr; - hdr->magic = MAGICWORD ^ (uintptr_t) hdr->next; - - /* And the next block. */ - if (hdr->next != NULL) - { - hdr->next->prev = hdr; - hdr->next->magic = MAGICWORD ^ ((uintptr_t) hdr - + (uintptr_t) hdr->next->next); - } -} -static void -freehook (void *ptr, const void *caller) -{ - if (pedantic) - mcheck_check_all (); - if (ptr) - { - struct hdr *hdr = ((struct hdr *) ptr) - 1; - checkhdr (hdr); - hdr->magic = MAGICFREE; - hdr->magic2 = MAGICFREE; - unlink_blk (hdr); - hdr->prev = hdr->next = NULL; - flood (ptr, FREEFLOOD, hdr->size); - ptr = hdr->block; - } - __free_hook = old_free_hook; - if (old_free_hook != NULL) - (*old_free_hook)(ptr, caller); - else - free (ptr); - __free_hook = freehook; -} - -static void * -mallochook (size_t size, const void *caller) -{ - struct hdr *hdr; - - if (pedantic) - mcheck_check_all (); - - if (size > ~((size_t) 0) - (sizeof (struct hdr) + 1)) - { - __set_errno (ENOMEM); - return NULL; - } - - __malloc_hook = old_malloc_hook; - if (old_malloc_hook != NULL) - hdr = (struct hdr *) (*old_malloc_hook)(sizeof (struct hdr) + size + 1, - caller); - else - hdr = (struct hdr *) malloc (sizeof (struct hdr) + size + 1); - __malloc_hook = mallochook; - if (hdr == NULL) - return NULL; - - hdr->size = size; - link_blk (hdr); - hdr->block = hdr; - hdr->magic2 = (uintptr_t) hdr ^ MAGICWORD; - ((char *) &hdr[1])[size] = MAGICBYTE; - flood ((void *) (hdr + 1), MALLOCFLOOD, size); - return (void *) (hdr + 1); -} - -static void * -memalignhook (size_t alignment, size_t size, - const void *caller) -{ - struct hdr *hdr; - size_t slop; - char *block; - - if (pedantic) - mcheck_check_all (); - - slop = (sizeof *hdr + alignment - 1) & - alignment; - - if (size > ~((size_t) 0) - (slop + 1)) - { - __set_errno (ENOMEM); - return NULL; - } - - __memalign_hook = old_memalign_hook; - if (old_memalign_hook != NULL) - block = (*old_memalign_hook)(alignment, slop + size + 1, caller); - else - block = memalign (alignment, slop + size + 1); - __memalign_hook = memalignhook; - if (block == NULL) - return NULL; - - hdr = ((struct hdr *) (block + slop)) - 1; - - hdr->size = size; - link_blk (hdr); - hdr->block = (void *) block; - hdr->magic2 = (uintptr_t) block ^ MAGICWORD; - ((char *) &hdr[1])[size] = MAGICBYTE; - flood ((void *) (hdr + 1), MALLOCFLOOD, size); - return (void *) (hdr + 1); -} - -static void * -reallochook (void *ptr, size_t size, const void *caller) -{ - if (size == 0) - { - freehook (ptr, caller); - return NULL; - } - - struct hdr *hdr; - size_t osize; - - if (pedantic) - mcheck_check_all (); - - if (size > ~((size_t) 0) - (sizeof (struct hdr) + 1)) - { - __set_errno (ENOMEM); - return NULL; - } - - if (ptr) - { - hdr = ((struct hdr *) ptr) - 1; - osize = hdr->size; - - checkhdr (hdr); - unlink_blk (hdr); - if (size < osize) - flood ((char *) ptr + size, FREEFLOOD, osize - size); - } - else - { - osize = 0; - hdr = NULL; - } - __free_hook = old_free_hook; - __malloc_hook = old_malloc_hook; - __memalign_hook = old_memalign_hook; - __realloc_hook = old_realloc_hook; - if (old_realloc_hook != NULL) - hdr = (struct hdr *) (*old_realloc_hook)((void *) hdr, - sizeof (struct hdr) + size + 1, - caller); - else - hdr = (struct hdr *) realloc ((void *) hdr, - sizeof (struct hdr) + size + 1); - __free_hook = freehook; - __malloc_hook = mallochook; - __memalign_hook = memalignhook; - __realloc_hook = reallochook; - if (hdr == NULL) - return NULL; - - hdr->size = size; - link_blk (hdr); - hdr->block = hdr; - hdr->magic2 = (uintptr_t) hdr ^ MAGICWORD; - ((char *) &hdr[1])[size] = MAGICBYTE; - if (size > osize) - flood ((char *) (hdr + 1) + osize, MALLOCFLOOD, size - osize); - return (void *) (hdr + 1); -} - -__attribute__ ((noreturn)) -static void -mabort (enum mcheck_status status) -{ - const char *msg; - switch (status) - { - case MCHECK_OK: - msg = _ ("memory is consistent, library is buggy\n"); - break; - case MCHECK_HEAD: - msg = _ ("memory clobbered before allocated block\n"); - break; - case MCHECK_TAIL: - msg = _ ("memory clobbered past end of allocated block\n"); - break; - case MCHECK_FREE: - msg = _ ("block freed twice\n"); - break; - default: - msg = _ ("bogus mcheck_status, library is buggy\n"); - break; - } -#ifdef _LIBC - __libc_fatal (msg); -#else - fprintf (stderr, "mcheck: %s", msg); - fflush (stderr); - abort (); -#endif -} - -/* Memory barrier so that GCC does not optimize out the argument. */ -#define malloc_opt_barrier(x) \ - ({ __typeof (x) __x = x; __asm ("" : "+m" (__x)); __x; }) int mcheck (void (*func) (enum mcheck_status)) { - abortfunc = (func != NULL) ? func : &mabort; - - /* These hooks may not be safely inserted if malloc is already in use. */ - if (__malloc_initialized <= 0 && !mcheck_used) - { - /* We call malloc() once here to ensure it is initialized. */ - void *p = malloc (0); - /* GCC might optimize out the malloc/free pair without a barrier. */ - p = malloc_opt_barrier (p); - free (p); - - old_free_hook = __free_hook; - __free_hook = freehook; - old_malloc_hook = __malloc_hook; - __malloc_hook = mallochook; - old_memalign_hook = __memalign_hook; - __memalign_hook = memalignhook; - old_realloc_hook = __realloc_hook; - __realloc_hook = reallochook; - mcheck_used = 1; - } - - return mcheck_used ? 0 : -1; + return 0; } #ifdef _LIBC libc_hidden_def (mcheck) @@ -403,14 +48,11 @@ libc_hidden_def (mcheck) int mcheck_pedantic (void (*func) (enum mcheck_status)) { - int res = mcheck (func); - if (res == 0) - pedantic = 1; - return res; + return 0; } enum mcheck_status mprobe (void *ptr) { - return mcheck_used ? checkhdr (((struct hdr *) ptr) - 1) : MCHECK_DISABLED; + return MCHECK_DISABLED; } diff --git a/malloc/morecore.c b/malloc/morecore.c index 165de7e386..b8242ceaff 100644 --- a/malloc/morecore.c +++ b/malloc/morecore.c @@ -42,7 +42,7 @@ libc_hidden_proto (__sbrk) and return the start of data space, or NULL on errors. If INCREMENT is negative, shrink data space. */ void * -__default_morecore (ptrdiff_t increment) +__glibc_morecore (ptrdiff_t increment) { void *result = (void *) __sbrk (increment); if (result == (void *) -1) @@ -50,4 +50,6 @@ __default_morecore (ptrdiff_t increment) return result; } -libc_hidden_def (__default_morecore) +#if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_28) +compat_symbol (libc, __glibc_morecore, __default_morecore, GLIBC_2_0); +#endif diff --git a/malloc/mtrace.c b/malloc/mtrace.c index 9064f209ec..dd92e3f92d 100644 --- a/malloc/mtrace.c +++ b/malloc/mtrace.c @@ -37,30 +37,12 @@ #include #include -#define setvbuf(s, b, f, l) _IO_setvbuf (s, b, f, l) -#define fwrite(buf, size, count, fp) _IO_fwrite (buf, size, count, fp) #include -#define TRACE_BUFFER_SIZE 512 - -static FILE *mallstream; -static const char mallenv[] = "MALLOC_TRACE"; -static char *malloc_trace_buffer; - -__libc_lock_define_initialized (static, lock); - /* Address to breakpoint on accesses to... */ void *mallwatch; -/* Old hook values. */ -static void (*tr_old_free_hook) (void *ptr, const void *); -static void *(*tr_old_malloc_hook) (size_t size, const void *); -static void *(*tr_old_realloc_hook) (void *ptr, size_t size, - const void *); -static void *(*tr_old_memalign_hook) (size_t __alignment, size_t __size, - const void *); - /* This function is called when the block being alloc'd, realloc'd, or freed has an address matching the variable "mallwatch". In a debugger, set "mallwatch" to the address of interest, then put a breakpoint on @@ -74,275 +56,20 @@ tr_break (void) } libc_hidden_def (tr_break) -static void -tr_where (const void *caller, Dl_info *info) -{ - if (caller != NULL) - { - if (info != NULL) - { - char *buf = (char *) ""; - if (info->dli_sname != NULL) - { - size_t len = strlen (info->dli_sname); - buf = alloca (len + 6 + 2 * sizeof (void *)); - - buf[0] = '('; - __stpcpy (_fitoa (caller >= (const void *) info->dli_saddr - ? caller - (const void *) info->dli_saddr - : (const void *) info->dli_saddr - caller, - __stpcpy (__mempcpy (buf + 1, info->dli_sname, - len), - caller >= (void *) info->dli_saddr - ? "+0x" : "-0x"), - 16, 0), - ")"); - } - - fprintf (mallstream, "@ %s%s%s[%p] ", - info->dli_fname ? : "", info->dli_fname ? ":" : "", - buf, caller); - } - else - fprintf (mallstream, "@ [%p] ", caller); - } -} - -static Dl_info * -lock_and_info (const void *caller, Dl_info *mem) -{ - if (caller == NULL) - return NULL; - - Dl_info *res = _dl_addr (caller, mem, NULL, NULL) ? mem : NULL; - - __libc_lock_lock (lock); - - return res; -} - -static void -tr_freehook (void *ptr, const void *caller) -{ - if (ptr == NULL) - return; - - Dl_info mem; - Dl_info *info = lock_and_info (caller, &mem); - tr_where (caller, info); - /* Be sure to print it first. */ - fprintf (mallstream, "- %p\n", ptr); - if (ptr == mallwatch) - { - __libc_lock_unlock (lock); - tr_break (); - __libc_lock_lock (lock); - } - __free_hook = tr_old_free_hook; - if (tr_old_free_hook != NULL) - (*tr_old_free_hook)(ptr, caller); - else - free (ptr); - __free_hook = tr_freehook; - __libc_lock_unlock (lock); -} - -static void * -tr_mallochook (size_t size, const void *caller) -{ - void *hdr; - - Dl_info mem; - Dl_info *info = lock_and_info (caller, &mem); - - __malloc_hook = tr_old_malloc_hook; - if (tr_old_malloc_hook != NULL) - hdr = (void *) (*tr_old_malloc_hook)(size, caller); - else - hdr = (void *) malloc (size); - __malloc_hook = tr_mallochook; - - tr_where (caller, info); - /* We could be printing a NULL here; that's OK. */ - fprintf (mallstream, "+ %p %#lx\n", hdr, (unsigned long int) size); - - __libc_lock_unlock (lock); - - if (hdr == mallwatch) - tr_break (); - - return hdr; -} - -static void * -tr_reallochook (void *ptr, size_t size, const void *caller) -{ - void *hdr; - - if (ptr == mallwatch) - tr_break (); - - Dl_info mem; - Dl_info *info = lock_and_info (caller, &mem); - - __free_hook = tr_old_free_hook; - __malloc_hook = tr_old_malloc_hook; - __realloc_hook = tr_old_realloc_hook; - if (tr_old_realloc_hook != NULL) - hdr = (void *) (*tr_old_realloc_hook)(ptr, size, caller); - else - hdr = (void *) realloc (ptr, size); - __free_hook = tr_freehook; - __malloc_hook = tr_mallochook; - __realloc_hook = tr_reallochook; - - tr_where (caller, info); - if (hdr == NULL) - { - if (size != 0) - /* Failed realloc. */ - fprintf (mallstream, "! %p %#lx\n", ptr, (unsigned long int) size); - else - fprintf (mallstream, "- %p\n", ptr); - } - else if (ptr == NULL) - fprintf (mallstream, "+ %p %#lx\n", hdr, (unsigned long int) size); - else - { - fprintf (mallstream, "< %p\n", ptr); - tr_where (caller, info); - fprintf (mallstream, "> %p %#lx\n", hdr, (unsigned long int) size); - } - - __libc_lock_unlock (lock); - - if (hdr == mallwatch) - tr_break (); - - return hdr; -} - -static void * -tr_memalignhook (size_t alignment, size_t size, const void *caller) -{ - void *hdr; - - Dl_info mem; - Dl_info *info = lock_and_info (caller, &mem); - - __memalign_hook = tr_old_memalign_hook; - __malloc_hook = tr_old_malloc_hook; - if (tr_old_memalign_hook != NULL) - hdr = (void *) (*tr_old_memalign_hook)(alignment, size, caller); - else - hdr = (void *) memalign (alignment, size); - __memalign_hook = tr_memalignhook; - __malloc_hook = tr_mallochook; - - tr_where (caller, info); - /* We could be printing a NULL here; that's OK. */ - fprintf (mallstream, "+ %p %#lx\n", hdr, (unsigned long int) size); - - __libc_lock_unlock (lock); - - if (hdr == mallwatch) - tr_break (); - - return hdr; -} - - -#ifdef _LIBC - -/* This function gets called to make sure all memory the library - allocates get freed and so does not irritate the user when studying - the mtrace output. */ -static void __libc_freeres_fn_section -release_libc_mem (void) -{ - /* Only call the free function if we still are running in mtrace mode. */ - if (mallstream != NULL) - __libc_freeres (); -} -#endif - - /* We enable tracing if either the environment variable MALLOC_TRACE is set, or if the variable mallwatch has been patched to an address that the debugging user wants us to stop on. When patching mallwatch, - don't forget to set a breakpoint on tr_break! */ + don't forget to set a breakpoint on tr_break! In all of these cases + we call mtrace() and that is interposed by libmalloc-extras.so. */ void mtrace (void) { -#ifdef _LIBC - static int added_atexit_handler; -#endif - char *mallfile; - - /* Don't panic if we're called more than once. */ - if (mallstream != NULL) - return; - -#ifdef _LIBC - /* When compiling the GNU libc we use the secure getenv function - which prevents the misuse in case of SUID or SGID enabled - programs. */ - mallfile = __libc_secure_getenv (mallenv); -#else - mallfile = getenv (mallenv); -#endif - if (mallfile != NULL || mallwatch != NULL) - { - char *mtb = malloc (TRACE_BUFFER_SIZE); - if (mtb == NULL) - return; - - mallstream = fopen (mallfile != NULL ? mallfile : "/dev/null", "wce"); - if (mallstream != NULL) - { - /* Be sure it doesn't malloc its buffer! */ - malloc_trace_buffer = mtb; - setvbuf (mallstream, malloc_trace_buffer, _IOFBF, TRACE_BUFFER_SIZE); - fprintf (mallstream, "= Start\n"); - tr_old_free_hook = __free_hook; - __free_hook = tr_freehook; - tr_old_malloc_hook = __malloc_hook; - __malloc_hook = tr_mallochook; - tr_old_realloc_hook = __realloc_hook; - __realloc_hook = tr_reallochook; - tr_old_memalign_hook = __memalign_hook; - __memalign_hook = tr_memalignhook; -#ifdef _LIBC - if (!added_atexit_handler) - { - added_atexit_handler = 1; - __cxa_atexit ((void (*)(void *))release_libc_mem, NULL, - __dso_handle); - } -#endif - } - else - free (mtb); - } + return; } void muntrace (void) { - if (mallstream == NULL) - return; - - /* Do the reverse of what done in mtrace: first reset the hooks and - MALLSTREAM, and only after that write the trailer and close the - file. */ - FILE *f = mallstream; - mallstream = NULL; - __free_hook = tr_old_free_hook; - __malloc_hook = tr_old_malloc_hook; - __realloc_hook = tr_old_realloc_hook; - __memalign_hook = tr_old_memalign_hook; - - fprintf (f, "= End\n"); - fclose (f); + return; } diff --git a/malloc/tst-malloc-usable-static-tunables.c b/malloc/tst-malloc-usable-static-tunables.c deleted file mode 100644 index 8907db01a5..0000000000 --- a/malloc/tst-malloc-usable-static-tunables.c +++ /dev/null @@ -1 +0,0 @@ -#include diff --git a/malloc/tst-malloc-usable-static.c b/malloc/tst-malloc-usable-static.c deleted file mode 100644 index 8907db01a5..0000000000 --- a/malloc/tst-malloc-usable-static.c +++ /dev/null @@ -1 +0,0 @@ -#include diff --git a/malloc/tst-malloc-weak-usable-static.c b/malloc/tst-malloc-weak-usable-static.c new file mode 100644 index 0000000000..a8b9a6652f --- /dev/null +++ b/malloc/tst-malloc-weak-usable-static.c @@ -0,0 +1 @@ +#include diff --git a/malloc/tst-malloc-weak-usable.c b/malloc/tst-malloc-weak-usable.c new file mode 100644 index 0000000000..70f939851b --- /dev/null +++ b/malloc/tst-malloc-weak-usable.c @@ -0,0 +1,60 @@ +/* Ensure that malloc_usable_size returns the request size with + MALLOC_CHECK_ exported to a positive value and no preloaded + libmalloc-extras.so. + + Copyright (C) 2012-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 + +/* In this test we are purposely not loading libmalloc-extras.so + to test that the malloc_usable_size() correctly falls back to + the chunk size, and does not try to parse the check data which + is not present. */ + +static int +do_test (void) +{ + size_t usable_size; + void *p = malloc (5); + if (!p) + { + printf ("memory allocation failed\n"); + return 1; + } + + usable_size = malloc_usable_size (p); + + /* Set a reasonably wide test check here. The point is that + without the checking code we are going to get the chunk + size back, and that might vary by architecture in theory. + On a 64-bit arch it should be no larger than 32-bytes given + the present struct layout, and likely 24 bytes. */ + if (usable_size < 5 || usable_size > 32) + { + printf ("malloc_usable_size: expected > 5 || < 32 but got %zu\n", usable_size); + return 1; + } + + memset (p, 0, usable_size); + free (p); + return 0; +} + +#include diff --git a/malloc/tst-mtrace.sh b/malloc/tst-mtrace.sh index 9c50c0675d..926c96e3bb 100755 --- a/malloc/tst-mtrace.sh +++ b/malloc/tst-mtrace.sh @@ -30,6 +30,7 @@ trap "rm -f ${common_objpfx}malloc/tst-mtrace.leak; exit 1" 1 2 15 ${test_program_prefix_before_env} \ ${run_program_env} \ MALLOC_TRACE=${common_objpfx}malloc/tst-mtrace.leak \ +LD_PRELOAD=${common_objpfx}/malloc/libmalloc-extras.so \ ${test_program_prefix_after_env} \ ${common_objpfx}malloc/tst-mtrace || status=1 diff --git a/manual/memory.texi b/manual/memory.texi index a1435aad1a..0997412dd7 100644 --- a/manual/memory.texi +++ b/manual/memory.texi @@ -328,8 +328,6 @@ any time (or never). * Malloc Tunable Parameters:: Use @code{mallopt} to adjust allocation parameters. * Heap Consistency Checking:: Automatic checking for errors. -* Hooks for Malloc:: You can use these hooks for debugging - programs that use @code{malloc}. * Statistics of Malloc:: Getting information about how much memory your program is using. * Summary of Malloc:: Summary of @code{malloc} and related functions. @@ -347,21 +345,21 @@ this function is in @file{stdlib.h}. @standards{ISO, malloc.h} @standards{ISO, stdlib.h} @safety{@prelim{}@mtsafe{}@asunsafe{@asulock{}}@acunsafe{@aculock{} @acsfd{} @acsmem{}}} -@c Malloc hooks and __morecore pointers, as well as such parameters as -@c max_n_mmaps and max_mmapped_mem, are accessed without guards, so they -@c could pose a thread safety issue; in order to not declare malloc -@c MT-unsafe, it's modifying the hooks and parameters while multiple -@c threads are active that is regarded as unsafe. An arena's next field -@c is initialized and never changed again, except for main_arena's, -@c that's protected by list_lock; next_free is only modified while -@c list_lock is held too. All other data members of an arena, as well -@c as the metadata of the memory areas assigned to it, are only modified -@c while holding the arena's mutex (fastbin pointers use catomic ops -@c because they may be modified by free without taking the arena's -@c lock). Some reassurance was needed for fastbins, for it wasn't clear -@c how they were initialized. It turns out they are always -@c zero-initialized: main_arena's, for being static data, and other -@c arena's, for being just-mmapped memory. + +@c Malloc parameters such as max_n_mmaps and max_mmapped_mem, are +@c accessed without guards, so they could pose a thread safety issue; in +@c order to not declare malloc MT-unsafe, it's modifying the hooks and +@c parameters while multiple threads are active that is regarded as +@c unsafe. An arena's next field is initialized and never changed +@c again, except for main_arena's, that's protected by list_lock; +@c next_free is only modified while list_lock is held too. All other +@c data members of an arena, as well as the metadata of the memory areas +@c assigned to it, are only modified while holding the arena's mutex +@c (fastbin pointers use catomic ops because they may be modified by +@c free without taking the arena's lock). Some reassurance was needed +@c for fastbins, for it wasn't clear how they were initialized. It +@c turns out they are always zero-initialized: main_arena's, for being +@c static data, and other arena's, for being just-mmapped memory. @c Leaking file descriptors and memory in case of cancellation is @c unavoidable without disabling cancellation, but the lock situation is @@ -375,7 +373,6 @@ this function is in @file{stdlib.h}. @c __libc_malloc @asulock @aculock @acsfd @acsmem @c force_reg ok -@c *malloc_hook unguarded @c arena_lock @asulock @aculock @acsfd @acsmem @c mutex_lock @asulock @aculock @c arena_get2 @asulock @aculock @acsfd @acsmem @@ -506,11 +503,8 @@ this function is in @file{stdlib.h}. @c set_foot dup ok @c contiguous ok @c MORECORE ok -@c *__morecore ok unguarded -@c __default_morecore @c sbrk ok @c force_reg dup ok -@c *__after_morecore_hook unguarded @c set_noncontiguous ok @c malloc_printerr dup ok @c _int_free (have_lock) @acsfd @acsmem [@asulock @aculock] @@ -543,7 +537,6 @@ this function is in @file{stdlib.h}. @c malloc_consolidate dup ok @c systrim ok @c MORECORE dup ok -@c *__after_morecore_hook dup unguarded @c set_head dup ok @c check_malloc_state ok/disabled @c top dup ok @@ -695,7 +688,6 @@ The prototype for this function is in @file{stdlib.h}. @c each gets a signal whose handler free()s large (non-fastbin-able) @c blocks from each other's arena, we deadlock; this is a more general @c case of @asulock. -@c *__free_hook unguarded @c mem2chunk ok @c chunk_is_mmapped ok, chunk bits not modified after allocation @c chunksize ok @@ -764,7 +756,6 @@ You can make the block longer by calling @code{realloc} or @c issues arise, plus the realloc hook, also accessed without guards. @c __libc_realloc @asulock @aculock @acsfd @acsmem -@c *__realloc_hook unguarded @c __libc_free dup @asulock @aculock @acsfd @acsmem @c __libc_malloc dup @asulock @aculock @acsfd @acsmem @c mem2chunk dup ok @@ -885,7 +876,6 @@ is declared in @file{stdlib.h}. @c Same caveats as malloc. @c __libc_calloc @asulock @aculock @acsfd @acsmem -@c *__malloc_hook dup unguarded @c memset dup ok @c arena_get @asulock @aculock @acsfd @acsmem @c arena_lock dup @asulock @aculock @acsfd @acsmem @@ -967,7 +957,6 @@ portability to modern non-POSIX systems than @code{posix_memalign}. @c _int_memalign, with the arena still locked. @c __libc_memalign @asulock @aculock @acsfd @acsmem -@c *__memalign_hook dup unguarded @c __libc_malloc dup @asulock @aculock @acsfd @acsmem @c arena_get dup @asulock @aculock @acsfd @acsmem @c _int_memalign @acsfd @acsmem @@ -1056,9 +1045,7 @@ systems that do not support @w{ISO C11}. @c next_env_entry ok @c strcspn dup ok @c __libc_mallopt dup @mtasuconst:mallopt [setting mp_] -@c __malloc_check_init @mtasuconst:malloc_hooks [setting hooks] @c *__malloc_initialize_hook unguarded, ok -@c *__memalign_hook dup ok, unguarded @c arena_get dup @asulock @aculock @acsfd @acsmem @c _int_valloc @acsfd @acsmem @c malloc_consolidate dup ok @@ -1206,9 +1193,13 @@ using the @code{mcheck} function. This function is a GNU extension, declared in @file{mcheck.h}. @pindex mcheck.h +The functionality associated with @code{MALLOC_CHECK_} or @code{mcheck} +is disabled by default. To enable the functionality you must preload +the @code{libmalloc-extras.so} library. + @deftypefun int mcheck (void (*@var{abortfn}) (enum mcheck_status @var{status})) @standards{GNU, mcheck.h} -@safety{@prelim{}@mtunsafe{@mtasurace{:mcheck} @mtasuconst{:malloc_hooks}}@asunsafe{@asucorrupt{}}@acunsafe{@acucorrupt{}}} +@safety{@prelim{}@mtunsafe{@mtasurace{:mcheck}}@asunsafe{@asucorrupt{}}@acunsafe{@acucorrupt{}}} @c The hooks must be set up before malloc is first used, which sort of @c implies @mtuinit/@asuinit but since the function is a no-op if malloc @c was already used, that doesn't pose any safety issues. The actual @@ -1261,7 +1252,7 @@ must be called before the first such function. @end deftypefun @deftypefun {enum mcheck_status} mprobe (void *@var{pointer}) -@safety{@prelim{}@mtunsafe{@mtasurace{:mcheck} @mtasuconst{:malloc_hooks}}@asunsafe{@asucorrupt{}}@acunsafe{@acucorrupt{}}} +@safety{@prelim{}@mtunsafe{@mtasurace{:mcheck}}@asunsafe{@asucorrupt{}}@acunsafe{@acucorrupt{}}} @c The linked list of headers may be modified concurrently by other @c threads, and it may find a partial update if called from a signal @c handler. It's mostly read only, so cancelling it might be safe, but @@ -1334,173 +1325,6 @@ compatibility. Both @code{MALLOC_CHECK_} and @samp{-lmcheck} should uncover the same bugs - but using @code{MALLOC_CHECK_} you don't need to recompile your application. -@node Hooks for Malloc -@subsubsection Memory Allocation Hooks -@cindex allocation hooks, for @code{malloc} - -@Theglibc{} lets you modify the behavior of @code{malloc}, -@code{realloc}, and @code{free} by specifying appropriate hook -functions. You can use these hooks to help you debug programs that use -dynamic memory allocation, for example. - -The hook variables are declared in @file{malloc.h}. -@pindex malloc.h - -@defvar __malloc_hook -@standards{GNU, malloc.h} -The value of this variable is a pointer to the function that -@code{malloc} uses whenever it is called. You should define this -function to look like @code{malloc}; that is, like: - -@smallexample -void *@var{function} (size_t @var{size}, const void *@var{caller}) -@end smallexample - -The value of @var{caller} is the return address found on the stack when -the @code{malloc} function was called. This value allows you to trace -the memory consumption of the program. -@end defvar - -@defvar __realloc_hook -@standards{GNU, malloc.h} -The value of this variable is a pointer to function that @code{realloc} -uses whenever it is called. You should define this function to look -like @code{realloc}; that is, like: - -@smallexample -void *@var{function} (void *@var{ptr}, size_t @var{size}, const void *@var{caller}) -@end smallexample - -The value of @var{caller} is the return address found on the stack when -the @code{realloc} function was called. This value allows you to trace the -memory consumption of the program. -@end defvar - -@defvar __free_hook -@standards{GNU, malloc.h} -The value of this variable is a pointer to function that @code{free} -uses whenever it is called. You should define this function to look -like @code{free}; that is, like: - -@smallexample -void @var{function} (void *@var{ptr}, const void *@var{caller}) -@end smallexample - -The value of @var{caller} is the return address found on the stack when -the @code{free} function was called. This value allows you to trace the -memory consumption of the program. -@end defvar - -@defvar __memalign_hook -@standards{GNU, malloc.h} -The value of this variable is a pointer to function that @code{aligned_alloc}, -@code{memalign}, @code{posix_memalign} and @code{valloc} use whenever they -are called. You should define this function to look like @code{aligned_alloc}; -that is, like: - -@smallexample -void *@var{function} (size_t @var{alignment}, size_t @var{size}, const void *@var{caller}) -@end smallexample - -The value of @var{caller} is the return address found on the stack when -the @code{aligned_alloc}, @code{memalign}, @code{posix_memalign} or -@code{valloc} functions are called. This value allows you to trace the -memory consumption of the program. -@end defvar - -You must make sure that the function you install as a hook for one of -these functions does not call that function recursively without restoring -the old value of the hook first! Otherwise, your program will get stuck -in an infinite recursion. Before calling the function recursively, one -should make sure to restore all the hooks to their previous value. When -coming back from the recursive call, all the hooks should be resaved -since a hook might modify itself. - -An issue to look out for is the time at which the malloc hook functions -can be safely installed. If the hook functions call the malloc-related -functions recursively, it is necessary that malloc has already properly -initialized itself at the time when @code{__malloc_hook} etc. is -assigned to. On the other hand, if the hook functions provide a -complete malloc implementation of their own, it is vital that the hooks -are assigned to @emph{before} the very first @code{malloc} call has -completed, because otherwise a chunk obtained from the ordinary, -un-hooked malloc may later be handed to @code{__free_hook}, for example. - -Here is an example showing how to use @code{__malloc_hook} and -@code{__free_hook} properly. It installs a function that prints out -information every time @code{malloc} or @code{free} is called. We just -assume here that @code{realloc} and @code{memalign} are not used in our -program. - -@smallexample -/* Prototypes for __malloc_hook, __free_hook */ -#include - -/* Prototypes for our hooks. */ -static void my_init_hook (void); -static void *my_malloc_hook (size_t, const void *); -static void my_free_hook (void*, const void *); - -static void -my_init (void) -@{ - old_malloc_hook = __malloc_hook; - old_free_hook = __free_hook; - __malloc_hook = my_malloc_hook; - __free_hook = my_free_hook; -@} - -static void * -my_malloc_hook (size_t size, const void *caller) -@{ - void *result; - /* Restore all old hooks */ - __malloc_hook = old_malloc_hook; - __free_hook = old_free_hook; - /* Call recursively */ - result = malloc (size); - /* Save underlying hooks */ - old_malloc_hook = __malloc_hook; - old_free_hook = __free_hook; - /* @r{@code{printf} might call @code{malloc}, so protect it too.} */ - printf ("malloc (%u) returns %p\n", (unsigned int) size, result); - /* Restore our own hooks */ - __malloc_hook = my_malloc_hook; - __free_hook = my_free_hook; - return result; -@} - -static void -my_free_hook (void *ptr, const void *caller) -@{ - /* Restore all old hooks */ - __malloc_hook = old_malloc_hook; - __free_hook = old_free_hook; - /* Call recursively */ - free (ptr); - /* Save underlying hooks */ - old_malloc_hook = __malloc_hook; - old_free_hook = __free_hook; - /* @r{@code{printf} might call @code{free}, so protect it too.} */ - printf ("freed pointer %p\n", ptr); - /* Restore our own hooks */ - __malloc_hook = my_malloc_hook; - __free_hook = my_free_hook; -@} - -main () -@{ - my_init (); - @dots{} -@} -@end smallexample - -The @code{mcheck} function (@pxref{Heap Consistency Checking}) works by -installing such hooks. - -@c __morecore, __after_morecore_hook are undocumented -@c It's not clear whether to document them. - @node Statistics of Malloc @subsubsection Statistics for Memory Allocation with @code{malloc} @@ -1632,19 +1456,6 @@ Tell @code{malloc} to perform occasional consistency checks on dynamically allocated memory, and to call @var{abortfn} when an inconsistency is found. @xref{Heap Consistency Checking}. -@item void *(*__malloc_hook) (size_t @var{size}, const void *@var{caller}) -A pointer to a function that @code{malloc} uses whenever it is called. - -@item void *(*__realloc_hook) (void *@var{ptr}, size_t @var{size}, const void *@var{caller}) -A pointer to a function that @code{realloc} uses whenever it is called. - -@item void (*__free_hook) (void *@var{ptr}, const void *@var{caller}) -A pointer to a function that @code{free} uses whenever it is called. - -@item void (*__memalign_hook) (size_t @var{size}, size_t @var{alignment}, const void *@var{caller}) -A pointer to a function that @code{aligned_alloc}, @code{memalign}, -@code{posix_memalign} and @code{valloc} use whenever they are called. - @item struct mallinfo mallinfo (void) Return information about the current dynamic memory usage. @xref{Statistics of Malloc}. @@ -1667,6 +1478,10 @@ the location. To do this the application must be started in a special mode which is enabled by an environment variable. There are no speed penalties for the program if the debugging mode is not enabled. +The allocation debugging functionality is only safe for use in +single-threaded programs and requires preloading +@code{libmalloc-extras.so}. + @menu * Tracing malloc:: How to install the tracing functionality. * Using the Memory Debugger:: Example programs excerpts. @@ -1679,7 +1494,7 @@ penalties for the program if the debugging mode is not enabled. @deftypefun void mtrace (void) @standards{GNU, mcheck.h} -@safety{@prelim{}@mtunsafe{@mtsenv{} @mtasurace{:mtrace} @mtasuconst{:malloc_hooks} @mtuinit{}}@asunsafe{@asuinit{} @ascuheap{} @asucorrupt{} @asulock{}}@acunsafe{@acuinit{} @acucorrupt{} @aculock{} @acsfd{} @acsmem{}}} +@safety{@prelim{}@mtunsafe{@mtsenv{} @mtasurace{:mtrace} @mtuinit{}}@asunsafe{@asuinit{} @ascuheap{} @asucorrupt{} @asulock{}}@acunsafe{@acuinit{} @acucorrupt{} @aculock{} @acsfd{} @acsmem{}}} @c Like the mcheck hooks, these are not designed with thread safety in @c mind, because the hook pointers are temporarily modified without @c regard to other threads, signals or cancellation. @@ -1715,7 +1530,7 @@ systems. The prototype can be found in @file{mcheck.h}. @deftypefun void muntrace (void) @standards{GNU, mcheck.h} -@safety{@prelim{}@mtunsafe{@mtasurace{:mtrace} @mtasuconst{:malloc_hooks} @mtslocale{}}@asunsafe{@asucorrupt{} @ascuheap{}}@acunsafe{@acucorrupt{} @acsmem{} @aculock{} @acsfd{}}} +@safety{@prelim{}@mtunsafe{@mtasurace{:mtrace} @mtslocale{}}@asunsafe{@asucorrupt{} @ascuheap{}}@acunsafe{@acucorrupt{} @acsmem{} @aculock{} @acsfd{}}} @c muntrace @mtasurace:mtrace @mtslocale @asucorrupt @ascuheap @acucorrupt @acsmem @aculock @acsfd @c fprintf (fputs) dup @mtslocale @asucorrupt @ascuheap @acsmem @aculock @acucorrupt diff --git a/misc/Makefile b/misc/Makefile index b7be2bc19a..901b287468 100644 --- a/misc/Makefile +++ b/misc/Makefile @@ -135,13 +135,15 @@ $(objpfx)libg.a: $(dep-dummy-lib); $(make-dummy-lib) $(objpfx)tst-tsearch: $(libm) -tst-error1-ENV = MALLOC_TRACE=$(objpfx)tst-error1.mtrace +tst-error1-ENV = MALLOC_TRACE=$(objpfx)tst-error1.mtrace \ + LD_PRELOAD=$(objpfx)../malloc/libmalloc-extras.so tst-error1-ARGS = $(objpfx)tst-error1.out $(objpfx)tst-error1-mem.out: $(objpfx)tst-error1.out $(common-objpfx)malloc/mtrace $(objpfx)tst-error1.mtrace > $@; \ $(evaluate-test) -tst-allocate_once-ENV = MALLOC_TRACE=$(objpfx)tst-allocate_once.mtrace +tst-allocate_once-ENV = MALLOC_TRACE=$(objpfx)tst-allocate_once.mtrace \ + LD_PRELOAD=$(objpfx)../malloc/libmalloc-extras.so $(objpfx)tst-allocate_once-mem.out: $(objpfx)tst-allocate_once.out $(common-objpfx)malloc/mtrace $(objpfx)tst-allocate_once.mtrace > $@; \ $(evaluate-test) diff --git a/nptl/Makefile b/nptl/Makefile index 2f2bb0569d..4ee04ef620 100644 --- a/nptl/Makefile +++ b/nptl/Makefile @@ -529,10 +529,12 @@ tst-umask1-ARGS = $(objpfx)tst-umask1.temp $(objpfx)tst-atfork2: $(libdl) $(shared-thread-library) LDFLAGS-tst-atfork2 = -rdynamic -tst-atfork2-ENV = MALLOC_TRACE=$(objpfx)tst-atfork2.mtrace +tst-atfork2-ENV = MALLOC_TRACE=$(objpfx)tst-atfork2.mtrace \ + LD_PRELOAD=$(objpfx)../malloc/libmalloc-extras.so $(objpfx)tst-atfork2mod.so: $(shared-thread-library) -tst-stack3-ENV = MALLOC_TRACE=$(objpfx)tst-stack3.mtrace +tst-stack3-ENV = MALLOC_TRACE=$(objpfx)tst-stack3.mtrace \ + LD_PRELOAD=$(objpfx)/../malloc/libmalloc-extras.so $(objpfx)tst-stack3-mem.out: $(objpfx)tst-stack3.out $(common-objpfx)malloc/mtrace $(objpfx)tst-stack3.mtrace > $@; \ $(evaluate-test) diff --git a/posix/Makefile b/posix/Makefile index 989e42e1f8..8efa11bc34 100644 --- a/posix/Makefile +++ b/posix/Makefile @@ -278,43 +278,50 @@ annexc-CFLAGS = -O $(objpfx)annexc: annexc.c $(native-compile) -tst-fnmatch-ENV += MALLOC_TRACE=$(objpfx)tst-fnmatch.mtrace +tst-fnmatch-ENV += MALLOC_TRACE=$(objpfx)tst-fnmatch.mtrace \ + LD_PRELOAD=$(objpfx)../malloc/libmalloc-extras.so $(objpfx)tst-fnmatch-mem.out: $(objpfx)tst-fnmatch.out $(common-objpfx)malloc/mtrace $(objpfx)tst-fnmatch.mtrace > $@; \ $(evaluate-test) -bug-regex2-ENV = MALLOC_TRACE=$(objpfx)bug-regex2.mtrace +bug-regex2-ENV = MALLOC_TRACE=$(objpfx)bug-regex2.mtrace \ + LD_PRELOAD=$(objpfx)../malloc/libmalloc-extras.so $(objpfx)bug-regex2-mem.out: $(objpfx)bug-regex2.out $(common-objpfx)malloc/mtrace $(objpfx)bug-regex2.mtrace > $@; \ $(evaluate-test) -bug-regex14-ENV = MALLOC_TRACE=$(objpfx)bug-regex14.mtrace +bug-regex14-ENV = MALLOC_TRACE=$(objpfx)bug-regex14.mtrace \ + LD_PRELOAD=$(objpfx)../malloc/libmalloc-extras.so $(objpfx)bug-regex14-mem.out: $(objpfx)bug-regex14.out $(common-objpfx)malloc/mtrace $(objpfx)bug-regex14.mtrace > $@; \ $(evaluate-test) -bug-regex21-ENV = MALLOC_TRACE=$(objpfx)bug-regex21.mtrace +bug-regex21-ENV = MALLOC_TRACE=$(objpfx)bug-regex21.mtrace \ + LD_PRELOAD=$(objpfx)../malloc/libmalloc-extras.so $(objpfx)bug-regex21-mem.out: $(objpfx)bug-regex21.out $(common-objpfx)malloc/mtrace $(objpfx)bug-regex21.mtrace > $@; \ $(evaluate-test) -bug-regex31-ENV = MALLOC_TRACE=$(objpfx)bug-regex31.mtrace +bug-regex31-ENV = MALLOC_TRACE=$(objpfx)bug-regex31.mtrace \ + LD_PRELOAD=$(objpfx)../malloc/libmalloc-extras.so $(objpfx)bug-regex31-mem.out: $(objpfx)bug-regex31.out $(common-objpfx)malloc/mtrace $(objpfx)bug-regex31.mtrace > $@; \ $(evaluate-test) -bug-regex36-ENV = MALLOC_TRACE=$(objpfx)bug-regex36.mtrace +bug-regex36-ENV = MALLOC_TRACE=$(objpfx)bug-regex36.mtrace \ + LD_PRELOAD=$(objpfx)../malloc/libmalloc-extras.so $(objpfx)bug-regex36-mem.out: $(objpfx)bug-regex36.out $(common-objpfx)malloc/mtrace $(objpfx)bug-regex36.mtrace > $@; \ $(evaluate-test) -tst-vfork3-ENV = MALLOC_TRACE=$(objpfx)tst-vfork3.mtrace +tst-vfork3-ENV = MALLOC_TRACE=$(objpfx)tst-vfork3.mtrace \ + LD_PRELOAD=$(objpfx)../malloc/libmalloc-extras.so $(objpfx)tst-vfork3-mem.out: $(objpfx)tst-vfork3.out $(common-objpfx)malloc/mtrace $(objpfx)tst-vfork3.mtrace > $@; \ @@ -323,18 +330,21 @@ $(objpfx)tst-vfork3-mem.out: $(objpfx)tst-vfork3.out # tst-rxspencer.mtrace is not generated, only # tst-rxspencer-no-utf8.mtrace, since otherwise the file has almost # 100M and takes very long time to process. -tst-rxspencer-no-utf8-ENV += MALLOC_TRACE=$(objpfx)tst-rxspencer-no-utf8.mtrace +tst-rxspencer-no-utf8-ENV += MALLOC_TRACE=$(objpfx)tst-rxspencer-no-utf8.mtrace \ + LD_PRELOAD=$(objpfx)../malloc/libmalloc-extras.so $(objpfx)tst-rxspencer-no-utf8-mem.out: $(objpfx)tst-rxspencer-no-utf8.out $(common-objpfx)malloc/mtrace $(objpfx)tst-rxspencer-no-utf8.mtrace \ > $@; \ $(evaluate-test) -tst-pcre-ENV = MALLOC_TRACE=$(objpfx)tst-pcre.mtrace +tst-pcre-ENV = MALLOC_TRACE=$(objpfx)tst-pcre.mtrace \ + LD_PRELOAD=$(objpfx)../malloc/libmalloc-extras.so $(objpfx)tst-pcre-mem.out: $(objpfx)tst-pcre.out $(common-objpfx)malloc/mtrace $(objpfx)tst-pcre.mtrace > $@; \ $(evaluate-test) -tst-boost-ENV = MALLOC_TRACE=$(objpfx)tst-boost.mtrace +tst-boost-ENV = MALLOC_TRACE=$(objpfx)tst-boost.mtrace \ + LD_PRELOAD=$(objpfx)../malloc/libmalloc-extras.so $(objpfx)tst-boost-mem.out: $(objpfx)tst-boost.out $(common-objpfx)malloc/mtrace $(objpfx)tst-boost.mtrace > $@; \ $(evaluate-test) @@ -347,15 +357,18 @@ $(objpfx)bug-ga2-mem.out: $(objpfx)bug-ga2.out $(common-objpfx)malloc/mtrace $(objpfx)bug-ga2.mtrace > $@; \ $(evaluate-test) -bug-ga2-ENV = MALLOC_TRACE=$(objpfx)bug-ga2.mtrace +bug-ga2-ENV = MALLOC_TRACE=$(objpfx)bug-ga2.mtrace \ + LD_PRELOAD=$(objpfx)../malloc/libmalloc-extras.so -bug-glob2-ENV = MALLOC_TRACE=$(objpfx)bug-glob2.mtrace +bug-glob2-ENV = MALLOC_TRACE=$(objpfx)bug-glob2.mtrace \ + LD_PRELOAD=$(objpfx)../malloc/libmalloc-extras.so $(objpfx)bug-glob2-mem.out: $(objpfx)bug-glob2.out $(common-objpfx)malloc/mtrace $(objpfx)bug-glob2.mtrace > $@; \ $(evaluate-test) -tst-glob-tilde-ENV = MALLOC_TRACE=$(objpfx)tst-glob-tilde.mtrace +tst-glob-tilde-ENV = MALLOC_TRACE=$(objpfx)tst-glob-tilde.mtrace \ + LD_PRELOAD=$(objpfx)../malloc/libmalloc-extras.so $(objpfx)tst-glob-tilde-mem.out: $(objpfx)tst-glob-tilde.out $(common-objpfx)malloc/mtrace $(objpfx)tst-glob-tilde.mtrace > $@; \ diff --git a/posix/tst-vfork3.c b/posix/tst-vfork3.c index 0449b2486a..3ab6bdce49 100644 --- a/posix/tst-vfork3.c +++ b/posix/tst-vfork3.c @@ -147,6 +147,12 @@ do_prepare (void) FAIL_EXIT1 ("out of memory"); strcpy (stpcpy (tmpdirname, test_dir), "/tst-vfork3.XXXXXX"); + /* We have preloaded libmalloc-extras.so to implement mtrace(). + We unset LD_PRELOAD since the children run via /bin/sh which + uses the system libc which may lack the integration required + by libmalloc-extras.so. */ + unsetenv("LD_PRELOAD"); + tmpdirname = mkdtemp (tmpdirname); if (tmpdirname == NULL) FAIL_EXIT1 ("could not create temporary directory"); diff --git a/resolv/Makefile b/resolv/Makefile index ea395ac3eb..2668ee1c54 100644 --- a/resolv/Makefile +++ b/resolv/Makefile @@ -152,17 +152,20 @@ $(objpfx)tst-res_hconf_reorder: $(libdl) $(shared-thread-library) tst-res_hconf_reorder-ENV = RESOLV_REORDER=on $(objpfx)tst-leaks: $(objpfx)libresolv.so -tst-leaks-ENV = MALLOC_TRACE=$(objpfx)tst-leaks.mtrace +tst-leaks-ENV = MALLOC_TRACE=$(objpfx)tst-leaks.mtrace \ + LD_PRELOAD=$(objpfx)/../malloc/libmalloc-extras.so $(objpfx)mtrace-tst-leaks.out: $(objpfx)tst-leaks.out $(common-objpfx)malloc/mtrace $(objpfx)tst-leaks.mtrace > $@; \ $(evaluate-test) -tst-leaks2-ENV = MALLOC_TRACE=$(objpfx)tst-leaks2.mtrace +tst-leaks2-ENV = MALLOC_TRACE=$(objpfx)tst-leaks2.mtrace \ + LD_PRELOAD=$(objpfx)/../malloc/libmalloc-extras.so $(objpfx)mtrace-tst-leaks2.out: $(objpfx)tst-leaks2.out $(common-objpfx)malloc/mtrace $(objpfx)tst-leaks2.mtrace > $@; \ $(evaluate-test) -tst-resolv-res_ninit-ENV = MALLOC_TRACE=$(objpfx)tst-resolv-res_ninit.mtrace +tst-resolv-res_ninit-ENV = MALLOC_TRACE=$(objpfx)tst-resolv-res_ninit.mtrace \ + LD_PRELOAD=$(objpfx)/../malloc/libmalloc-extras.so $(objpfx)mtrace-tst-resolv-res_ninit.out: $(objpfx)tst-resolv-res_ninit.out $(common-objpfx)malloc/mtrace \ $(objpfx)tst-resolv-res_ninit.mtrace > $@; \ diff --git a/stdio-common/Makefile b/stdio-common/Makefile index 738a3cead0..bdb8578c40 100644 --- a/stdio-common/Makefile +++ b/stdio-common/Makefile @@ -91,9 +91,11 @@ $(objpfx)tst-swprintf.out: $(gen-locales) $(objpfx)tst-vfprintf-mbs-prec.out: $(gen-locales) endif -tst-printf-bz18872-ENV = MALLOC_TRACE=$(objpfx)tst-printf-bz18872.mtrace +tst-printf-bz18872-ENV = MALLOC_TRACE=$(objpfx)tst-printf-bz18872.mtrace \ + LD_PRELOAD=$(objpfx)../malloc/libmalloc-extras.so tst-vfprintf-width-prec-ENV = \ - MALLOC_TRACE=$(objpfx)tst-vfprintf-width-prec.mtrace + MALLOC_TRACE=$(objpfx)tst-vfprintf-width-prec.mtrace \ + LD_PRELOAD=$(objpfx)../malloc/libmalloc-extras.so $(objpfx)tst-unbputc.out: tst-unbputc.sh $(objpfx)tst-unbputc $(SHELL) $< $(common-objpfx) '$(test-program-prefix)' > $@; \