Skip to content

Commit ab0283a

Browse files
authored
Merge pull request #236 from struct/better_malloc_hooking
Better malloc hooking
2 parents 540a228 + ae87843 commit ab0283a

File tree

5 files changed

+113
-42
lines changed

5 files changed

+113
-42
lines changed

Makefile

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,10 @@ NAMED_MAPPINGS = -DNAMED_MAPPINGS=0
170170
## Abort when the allocator cannot return a valid chunk
171171
ABORT_ON_NULL = -DABORT_ON_NULL=0
172172

173+
## Abort when we detect a pointer not owned by IsoAlloc
174+
## Recommend setting this to 0 when using LD_PRELOAD
175+
ABORT_ON_UNOWNED_PTR = -DABORT_ON_UNOWNED_PTR=1
176+
173177
## Enable protection against misusing 0 sized allocations
174178
NO_ZERO_ALLOCATIONS = -DNO_ZERO_ALLOCATIONS=1
175179

@@ -316,7 +320,7 @@ BUILD_ERROR_FLAGS := $(BUILD_ERROR_FLAGS) -Wno-attributes -Wno-unused-variable
316320
endif
317321
CFLAGS += $(COMMON_CFLAGS) $(DISABLE_CANARY) $(BUILD_ERROR_FLAGS) $(HOOKS) $(HEAP_PROFILER) -fvisibility=hidden \
318322
-std=$(STDC) $(SANITIZER_SUPPORT) $(ALLOC_SANITY) $(MEMCPY_SANITY) $(UNINIT_READ_SANITY) $(CPU_PIN) $(SCHED_GETCPU) \
319-
$(EXPERIMENTAL) $(UAF_PTR_PAGE) $(VERIFY_FREE_BIT_SLOTS) $(NAMED_MAPPINGS) $(ABORT_ON_NULL) $(NO_ZERO_ALLOCATIONS) \
323+
$(EXPERIMENTAL) $(UAF_PTR_PAGE) $(VERIFY_FREE_BIT_SLOTS) $(NAMED_MAPPINGS) $(ABORT_ON_NULL) $(ABORT_ON_UNOWNED_PTR) $(NO_ZERO_ALLOCATIONS) \
320324
$(ABORT_NO_ENTROPY) $(ISO_DTOR_CLEANUP) $(RANDOMIZE_FREELIST) $(USE_SPINLOCK) $(HUGE_PAGES) ${THP_PAGES} $(USE_MLOCK) \
321325
$(MEMORY_TAGGING) $(STRONG_SIZE_ISOLATION) $(MEMSET_SANITY) $(AUTO_CTOR_DTOR) $(SIGNAL_HANDLER) \
322326
$(BIG_ZONE_META_DATA_GUARD) $(BIG_ZONE_GUARD) $(PROTECT_UNUSED_BIG_ZONE) $(MASK_PTRS) $(SANITIZE_CHUNKS) $(FUZZ_MODE) \
@@ -328,7 +332,7 @@ GDB_FLAGS = -g -ggdb3 -fno-omit-frame-pointer
328332
PERF_FLAGS = -pg -DPERF_TEST_BUILD=1
329333
LIBRARY = -fPIC -shared
330334
SRC_DIR = src
331-
C_SRCS = $(SRC_DIR)/*.c
335+
C_SRCS = $(filter-out $(SRC_DIR)/malloc_hook.c, $(wildcard $(SRC_DIR)/*.c))
332336
CXX_SRCS = $(SRC_DIR)/*.cpp
333337
ISO_ALLOC_PRINTF_SRC = $(SRC_DIR)/iso_alloc_printf.c
334338
BUILD_DIR = build
@@ -342,6 +346,16 @@ library: clean
342346
$(CC) $(CFLAGS) $(LIBRARY) $(OPTIMIZE) $(OS_FLAGS) $(C_SRCS) -o $(BUILD_DIR)/$(LIBNAME)
343347
$(STRIP)
344348

349+
## Build a release library suitable for LD_PRELOAD use.
350+
## ABORT_ON_UNOWNED_PTR=0 silently drops pointers not owned by isoalloc
351+
## (e.g. those allocated by libc before the isoalloc constructor fires)
352+
## instead of aborting. All other flags are identical to 'library'.
353+
library_perf: ABORT_ON_UNOWNED_PTR = -DABORT_ON_UNOWNED_PTR=0
354+
library_perf: clean
355+
@echo "make library_perf"
356+
$(CC) $(CFLAGS) $(LIBRARY) $(OPTIMIZE) $(OS_FLAGS) $(C_SRCS) -o $(BUILD_DIR)/$(LIBNAME)
357+
$(STRIP)
358+
345359
## Build a debug version of the library
346360
library_debug: clean
347361
@echo "make library debug"
@@ -410,6 +424,7 @@ tests: clean library_debug_unit_tests
410424
$(CC) $(CFLAGS) $(EXE_CFLAGS) $(DEBUG_LOG_FLAGS) $(GDB_FLAGS) $(OS_FLAGS) tests/pool_test.c $(ISO_ALLOC_PRINTF_SRC) -o $(BUILD_DIR)/pool_test $(LDFLAGS)
411425
utils/run_tests.sh
412426

427+
413428
mte_test: clean
414429
@echo "make mte_test"
415430
$(CC) $(CFLAGS) $(C_SRCS) $(DEBUG_LOG_FLAGS) $(GDB_FLAGS) $(EXE_CFLAGS) $(OS_FLAGS) tests/tests.c -o $(BUILD_DIR)/tests

include/iso_alloc_hook.h

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/* iso_alloc_hook.h - A secure memory allocator
2+
* Copyright 2023 - chris.rohlf@gmail.com */
3+
4+
#pragma once
5+
6+
/* Use direct function aliasing on GCC >= 9 and Clang >= 10.
7+
* This makes malloc/free/calloc/realloc etc. linker-level aliases
8+
* for their iso_ counterparts rather than thin wrapper functions.
9+
*
10+
* Benefits over wrapper functions:
11+
* - copy(fun) propagates all function attributes (malloc, alloc_size,
12+
* nothrow, etc.) from the iso_ target to the exported public symbol
13+
* - No wrapper call overhead; same code, two symbol names
14+
*
15+
* Falls back to inline wrapper bodies on older compilers.
16+
*
17+
* Note: alias() targets must live in the same final DSO. Both
18+
* malloc_hook.c and iso_alloc_interfaces.c are compiled into
19+
* libisoalloc.so, so the linker resolves the alias within the DSO. */
20+
21+
#if !defined(__APPLE__) && ((defined(__GNUC__) && __GNUC__ >= 9) || (defined(__clang__) && __clang_major__ >= 10))
22+
#pragma GCC diagnostic ignored "-Wattributes"
23+
#if defined(__GNUC__) && !defined(__clang__)
24+
#pragma GCC diagnostic ignored "-Wattribute-alias"
25+
#endif
26+
#define ISO_FORWARD(fun) __attribute__((alias(#fun), used, visibility("default"), copy(fun)));
27+
#define ISO_FORWARD0(fun, x) ISO_FORWARD(fun)
28+
#define ISO_FORWARD1(fun, x) ISO_FORWARD(fun)
29+
#define ISO_FORWARD2(fun, x, y) ISO_FORWARD(fun)
30+
#define ISO_FORWARD3(fun, x, y, z) ISO_FORWARD(fun)
31+
#else
32+
#define ISO_FORWARD0(fun, x) \
33+
{ fun(x); }
34+
#define ISO_FORWARD1(fun, x) \
35+
{ return fun(x); }
36+
#define ISO_FORWARD2(fun, x, y) \
37+
{ return fun(x, y); }
38+
#define ISO_FORWARD3(fun, x, y, z) \
39+
{ return fun(x, y, z); }
40+
#endif

src/iso_alloc.c

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1615,7 +1615,11 @@ INTERNAL_HIDDEN void _iso_free_size(void *p, size_t size) {
16151615
iso_alloc_big_zone_t *big_zone = iso_find_big_zone(p, true);
16161616

16171617
if(UNLIKELY(big_zone == NULL)) {
1618+
#if ABORT_ON_UNOWNED_PTR
16181619
LOG_AND_ABORT("Could not find any zone for allocation at 0x%p", p);
1620+
#else
1621+
return;
1622+
#endif
16191623
}
16201624

16211625
if(UNLIKELY(big_zone->size < size)) {
@@ -1631,7 +1635,12 @@ INTERNAL_HIDDEN void _iso_free_size(void *p, size_t size) {
16311635
iso_alloc_zone_t *zone = iso_find_zone_range(p);
16321636

16331637
if(UNLIKELY(zone == NULL)) {
1638+
#if ABORT_ON_UNOWNED_PTR
16341639
LOG_AND_ABORT("Could not find zone for 0x%p", p);
1640+
#else
1641+
UNLOCK_ROOT();
1642+
return;
1643+
#endif
16351644
}
16361645

16371646
/* We can't check for an exact size match because
@@ -1724,7 +1733,11 @@ INTERNAL_HIDDEN iso_alloc_zone_t *_iso_free_internal_unlocked(void *p, bool perm
17241733
iso_alloc_big_zone_t *big_zone = iso_find_big_zone(p, true);
17251734

17261735
if(UNLIKELY(big_zone == NULL)) {
1736+
#if ABORT_ON_UNOWNED_PTR
17271737
LOG_AND_ABORT("Could not find any zone for allocation at 0x%p", p);
1738+
#else
1739+
return NULL;
1740+
#endif
17281741
}
17291742

17301743
iso_free_big_zone(big_zone, permanent);
@@ -2044,15 +2057,18 @@ INTERNAL_HIDDEN size_t _iso_chunk_size(void *p) {
20442057

20452058
LOCK_ROOT();
20462059

2047-
/* We cannot return NULL here, we abort instead */
20482060
iso_alloc_zone_t *zone = iso_find_zone_range(p);
20492061

20502062
if(UNLIKELY(zone == NULL)) {
20512063
UNLOCK_ROOT();
20522064
iso_alloc_big_zone_t *big_zone = iso_find_big_zone(p, false);
20532065

20542066
if(UNLIKELY(big_zone == NULL)) {
2067+
#if ABORT_ON_UNOWNED_PTR
20552068
LOG_AND_ABORT("Could not find any zone for allocation at 0x%p", p);
2069+
#else
2070+
return 0;
2071+
#endif
20562072
}
20572073

20582074
return big_zone->size;

src/iso_alloc_interfaces.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,3 +319,9 @@ EXTERNAL_API FLATTEN void iso_alloc_search_stack(void *p) {
319319
_iso_alloc_search_stack(p);
320320
}
321321
#endif
322+
323+
/* Include malloc hooks here so alias targets (iso_alloc, iso_free, etc.)
324+
* are defined in the same translation unit. See malloc_hook.c for details. */
325+
#define ISO_IN_INTERFACES_C
326+
#include "malloc_hook.c"
327+
#undef ISO_IN_INTERFACES_C

src/malloc_hook.c

Lines changed: 33 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,16 @@
11
/* malloc_hook.c - Provides low level hooks for malloc/free
22
* Copyright 2023 - chris.rohlf@gmail.com */
33

4-
#include "iso_alloc.h"
5-
#include "iso_alloc_internal.h"
4+
/* This file must be included from iso_alloc_interfaces.c so that
5+
* alias targets (iso_alloc, iso_free, etc.) are defined in the same
6+
* translation unit. When compiled directly the guard
7+
* below produces an empty translation unit with no effect. */
8+
// clang-format off
9+
#if !defined(ISO_IN_INTERFACES_C)
10+
#error "malloc_hook.c must be included from iso_alloc_interfaces.c (so aliases can work)"
11+
#endif
12+
13+
#include "iso_alloc_hook.h"
614

715
/* The MALLOC_HOOK configuration allows us to hook the usual
816
* malloc interfaces and redirect them to the iso_alloc API.
@@ -13,45 +21,31 @@
1321
*/
1422
#if MALLOC_HOOK
1523

16-
EXTERNAL_API void *__libc_malloc(size_t s) {
17-
return iso_alloc(s);
18-
}
24+
/* malloc/free/calloc/realloc and their __libc_ variants are
25+
* direct linker aliases for the iso_ equivalents on GCC >= 9
26+
* and Clang >= 10 (see iso_alloc_hook.h). This propagates all
27+
* function attributes (malloc, alloc_size, nothrow, etc.) from
28+
* the iso_ symbol to the exported symbol via copy(fun), and
29+
* eliminates the wrapper call entirely.
30+
*
31+
* Functions with differing signatures or custom logic (posix_memalign,
32+
* aligned_alloc, memalign, malloc_size, malloc_good_size) remain
33+
* as wrapper functions. */
1934

20-
EXTERNAL_API void *malloc(size_t s) {
21-
return iso_alloc(s);
22-
}
35+
EXTERNAL_API void *__libc_malloc(size_t s) ISO_FORWARD1(iso_alloc, s)
36+
EXTERNAL_API void *malloc(size_t s) ISO_FORWARD1(iso_alloc, s)
2337

24-
EXTERNAL_API void __libc_free(void *p) {
25-
iso_free(p);
26-
}
38+
EXTERNAL_API void __libc_free(void *p) ISO_FORWARD0(iso_free, p)
39+
EXTERNAL_API void free(void *p) ISO_FORWARD0(iso_free, p)
2740

28-
EXTERNAL_API void free(void *p) {
29-
iso_free(p);
30-
}
41+
EXTERNAL_API void *__libc_calloc(size_t n, size_t s) ISO_FORWARD2(iso_calloc, n, s)
42+
EXTERNAL_API void *calloc(size_t n, size_t s) ISO_FORWARD2(iso_calloc, n, s)
3143

32-
EXTERNAL_API void *__libc_calloc(size_t n, size_t s) {
33-
return iso_calloc(n, s);
34-
}
44+
EXTERNAL_API void *__libc_realloc(void *p, size_t s) ISO_FORWARD2(iso_realloc, p, s)
45+
EXTERNAL_API void *realloc(void *p, size_t s) ISO_FORWARD2(iso_realloc, p, s)
3546

36-
EXTERNAL_API void *calloc(size_t n, size_t s) {
37-
return iso_calloc(n, s);
38-
}
39-
40-
EXTERNAL_API void *__libc_realloc(void *p, size_t s) {
41-
return iso_realloc(p, s);
42-
}
43-
44-
EXTERNAL_API void *realloc(void *p, size_t s) {
45-
return iso_realloc(p, s);
46-
}
47-
48-
EXTERNAL_API void *__libc_reallocarray(void *p, size_t n, size_t s) {
49-
return iso_reallocarray(p, n, s);
50-
}
51-
52-
EXTERNAL_API void *reallocarray(void *p, size_t n, size_t s) {
53-
return iso_reallocarray(p, n, s);
54-
}
47+
EXTERNAL_API void *__libc_reallocarray(void *p, size_t n, size_t s) ISO_FORWARD3(iso_reallocarray, p, n, s)
48+
EXTERNAL_API void *reallocarray(void *p, size_t n, size_t s) ISO_FORWARD3(iso_reallocarray, p, n, s)
5549

5650
EXTERNAL_API int __posix_memalign(void **r, size_t a, size_t s) {
5751
if(is_pow2(a) == false) {
@@ -104,9 +98,8 @@ EXTERNAL_API size_t malloc_good_size(size_t size) {
10498
return ALIGN_SZ_UP(size);
10599
}
106100
#else
107-
EXTERNAL_API size_t malloc_usable_size(void *ptr) {
108-
return iso_chunksz(ptr);
109-
}
101+
/* On Linux (non-Android) malloc_usable_size takes void* matching iso_chunksz */
102+
EXTERNAL_API size_t malloc_usable_size(void *ptr) ISO_FORWARD1(iso_chunksz, ptr)
110103
#endif
111104

112105
static void *libc_malloc(size_t s, const void *caller) {
@@ -129,3 +122,4 @@ void (*__free_hook)(void *, const void *) = &libc_free;
129122
void *(*__memalign_hook)(size_t, size_t, const void *) = &libc_memalign;
130123
#endif
131124
#endif
125+
// clang-format on

0 commit comments

Comments
 (0)