Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 17 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,10 @@ NAMED_MAPPINGS = -DNAMED_MAPPINGS=0
## Abort when the allocator cannot return a valid chunk
ABORT_ON_NULL = -DABORT_ON_NULL=0

## Abort when we detect a pointer not owned by IsoAlloc
## Recommend setting this to 0 when using LD_PRELOAD
ABORT_ON_UNOWNED_PTR = -DABORT_ON_UNOWNED_PTR=1

## Enable protection against misusing 0 sized allocations
NO_ZERO_ALLOCATIONS = -DNO_ZERO_ALLOCATIONS=1

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

## Build a release library suitable for LD_PRELOAD use.
## ABORT_ON_UNOWNED_PTR=0 silently drops pointers not owned by isoalloc
## (e.g. those allocated by libc before the isoalloc constructor fires)
## instead of aborting. All other flags are identical to 'library'.
library_perf: ABORT_ON_UNOWNED_PTR = -DABORT_ON_UNOWNED_PTR=0
library_perf: clean
@echo "make library_perf"
$(CC) $(CFLAGS) $(LIBRARY) $(OPTIMIZE) $(OS_FLAGS) $(C_SRCS) -o $(BUILD_DIR)/$(LIBNAME)
$(STRIP)

## Build a debug version of the library
library_debug: clean
@echo "make library debug"
Expand Down Expand Up @@ -410,6 +424,7 @@ tests: clean library_debug_unit_tests
$(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)
utils/run_tests.sh


mte_test: clean
@echo "make mte_test"
$(CC) $(CFLAGS) $(C_SRCS) $(DEBUG_LOG_FLAGS) $(GDB_FLAGS) $(EXE_CFLAGS) $(OS_FLAGS) tests/tests.c -o $(BUILD_DIR)/tests
Expand Down
40 changes: 40 additions & 0 deletions include/iso_alloc_hook.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/* iso_alloc_hook.h - A secure memory allocator
* Copyright 2023 - chris.rohlf@gmail.com */

#pragma once

/* Use direct function aliasing on GCC >= 9 and Clang >= 10.
* This makes malloc/free/calloc/realloc etc. linker-level aliases
* for their iso_ counterparts rather than thin wrapper functions.
*
* Benefits over wrapper functions:
* - copy(fun) propagates all function attributes (malloc, alloc_size,
* nothrow, etc.) from the iso_ target to the exported public symbol
* - No wrapper call overhead; same code, two symbol names
*
* Falls back to inline wrapper bodies on older compilers.
*
* Note: alias() targets must live in the same final DSO. Both
* malloc_hook.c and iso_alloc_interfaces.c are compiled into
* libisoalloc.so, so the linker resolves the alias within the DSO. */

#if !defined(__APPLE__) && ((defined(__GNUC__) && __GNUC__ >= 9) || (defined(__clang__) && __clang_major__ >= 10))
#pragma GCC diagnostic ignored "-Wattributes"
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic ignored "-Wattribute-alias"
#endif
#define ISO_FORWARD(fun) __attribute__((alias(#fun), used, visibility("default"), copy(fun)));
#define ISO_FORWARD0(fun, x) ISO_FORWARD(fun)
#define ISO_FORWARD1(fun, x) ISO_FORWARD(fun)
#define ISO_FORWARD2(fun, x, y) ISO_FORWARD(fun)
#define ISO_FORWARD3(fun, x, y, z) ISO_FORWARD(fun)
#else
#define ISO_FORWARD0(fun, x) \
{ fun(x); }
#define ISO_FORWARD1(fun, x) \
{ return fun(x); }
#define ISO_FORWARD2(fun, x, y) \
{ return fun(x, y); }
#define ISO_FORWARD3(fun, x, y, z) \
{ return fun(x, y, z); }
#endif
18 changes: 17 additions & 1 deletion src/iso_alloc.c
Original file line number Diff line number Diff line change
Expand Up @@ -1615,7 +1615,11 @@ INTERNAL_HIDDEN void _iso_free_size(void *p, size_t size) {
iso_alloc_big_zone_t *big_zone = iso_find_big_zone(p, true);

if(UNLIKELY(big_zone == NULL)) {
#if ABORT_ON_UNOWNED_PTR
LOG_AND_ABORT("Could not find any zone for allocation at 0x%p", p);
#else
return;
#endif
}

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

if(UNLIKELY(zone == NULL)) {
#if ABORT_ON_UNOWNED_PTR
LOG_AND_ABORT("Could not find zone for 0x%p", p);
#else
UNLOCK_ROOT();
return;
#endif
}

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

if(UNLIKELY(big_zone == NULL)) {
#if ABORT_ON_UNOWNED_PTR
LOG_AND_ABORT("Could not find any zone for allocation at 0x%p", p);
#else
return NULL;
#endif
}

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

LOCK_ROOT();

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

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

if(UNLIKELY(big_zone == NULL)) {
#if ABORT_ON_UNOWNED_PTR
LOG_AND_ABORT("Could not find any zone for allocation at 0x%p", p);
#else
return 0;
#endif
}

return big_zone->size;
Expand Down
6 changes: 6 additions & 0 deletions src/iso_alloc_interfaces.c
Original file line number Diff line number Diff line change
Expand Up @@ -319,3 +319,9 @@ EXTERNAL_API FLATTEN void iso_alloc_search_stack(void *p) {
_iso_alloc_search_stack(p);
}
#endif

/* Include malloc hooks here so alias targets (iso_alloc, iso_free, etc.)
* are defined in the same translation unit. See malloc_hook.c for details. */
#define ISO_IN_INTERFACES_C
#include "malloc_hook.c"
#undef ISO_IN_INTERFACES_C
72 changes: 33 additions & 39 deletions src/malloc_hook.c
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
/* malloc_hook.c - Provides low level hooks for malloc/free
* Copyright 2023 - chris.rohlf@gmail.com */

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

#include "iso_alloc_hook.h"

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

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

EXTERNAL_API void *malloc(size_t s) {
return iso_alloc(s);
}
EXTERNAL_API void *__libc_malloc(size_t s) ISO_FORWARD1(iso_alloc, s)
EXTERNAL_API void *malloc(size_t s) ISO_FORWARD1(iso_alloc, s)

EXTERNAL_API void __libc_free(void *p) {
iso_free(p);
}
EXTERNAL_API void __libc_free(void *p) ISO_FORWARD0(iso_free, p)
EXTERNAL_API void free(void *p) ISO_FORWARD0(iso_free, p)

EXTERNAL_API void free(void *p) {
iso_free(p);
}
EXTERNAL_API void *__libc_calloc(size_t n, size_t s) ISO_FORWARD2(iso_calloc, n, s)
EXTERNAL_API void *calloc(size_t n, size_t s) ISO_FORWARD2(iso_calloc, n, s)

EXTERNAL_API void *__libc_calloc(size_t n, size_t s) {
return iso_calloc(n, s);
}
EXTERNAL_API void *__libc_realloc(void *p, size_t s) ISO_FORWARD2(iso_realloc, p, s)
EXTERNAL_API void *realloc(void *p, size_t s) ISO_FORWARD2(iso_realloc, p, s)

EXTERNAL_API void *calloc(size_t n, size_t s) {
return iso_calloc(n, s);
}

EXTERNAL_API void *__libc_realloc(void *p, size_t s) {
return iso_realloc(p, s);
}

EXTERNAL_API void *realloc(void *p, size_t s) {
return iso_realloc(p, s);
}

EXTERNAL_API void *__libc_reallocarray(void *p, size_t n, size_t s) {
return iso_reallocarray(p, n, s);
}

EXTERNAL_API void *reallocarray(void *p, size_t n, size_t s) {
return iso_reallocarray(p, n, s);
}
EXTERNAL_API void *__libc_reallocarray(void *p, size_t n, size_t s) ISO_FORWARD3(iso_reallocarray, p, n, s)
EXTERNAL_API void *reallocarray(void *p, size_t n, size_t s) ISO_FORWARD3(iso_reallocarray, p, n, s)

EXTERNAL_API int __posix_memalign(void **r, size_t a, size_t s) {
if(is_pow2(a) == false) {
Expand Down Expand Up @@ -104,9 +98,8 @@ EXTERNAL_API size_t malloc_good_size(size_t size) {
return ALIGN_SZ_UP(size);
}
#else
EXTERNAL_API size_t malloc_usable_size(void *ptr) {
return iso_chunksz(ptr);
}
/* On Linux (non-Android) malloc_usable_size takes void* matching iso_chunksz */
EXTERNAL_API size_t malloc_usable_size(void *ptr) ISO_FORWARD1(iso_chunksz, ptr)
#endif

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