Skip to content

Commit 2ec29a6

Browse files
authored
Merge pull request #165 from struct/cleanup_uaf_ptr_page
Cleanup UAF_PTR_PAGE feature
2 parents 452e92f + 3fdfbd7 commit 2ec29a6

12 files changed

Lines changed: 81 additions & 50 deletions

.github/workflows/testsuite.yml

Lines changed: 1 addition & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -7,34 +7,6 @@ jobs:
77
- uses: actions/checkout@v2
88
- run: make tests
99
- run: make cpp_tests
10-
testsuite-clang7:
11-
runs-on: ubuntu-latest
12-
steps:
13-
- uses: actions/checkout@v2
14-
- run: sudo apt install clang-7
15-
- run: make tests
16-
- run: make cpp_tests
17-
testsuite-clang8:
18-
runs-on: ubuntu-latest
19-
steps:
20-
- uses: actions/checkout@v2
21-
- run: sudo apt install clang-8
22-
- run: make tests
23-
- run: make cpp_tests
24-
testsuite-clang9:
25-
runs-on: ubuntu-latest
26-
steps:
27-
- uses: actions/checkout@v2
28-
- run: sudo apt install clang-9
29-
- run: make tests
30-
- run: make cpp_tests
31-
testsuite-clang10:
32-
runs-on: ubuntu-latest
33-
steps:
34-
- uses: actions/checkout@v2
35-
- run: sudo apt install clang-10
36-
- run: make tests
37-
- run: make cpp_tests
3810
testsuite-clang11:
3911
runs-on: ubuntu-latest
4012
steps:
@@ -59,7 +31,7 @@ jobs:
5931
runs-on: ubuntu-latest
6032
steps:
6133
- uses: actions/checkout@v2
62-
- run: sudo apt install clang clang-format
34+
- run: sudo apt install clang clang-format-12
6335
- run: make format-ci
6436
testsuite-gcc:
6537
runs-on: ubuntu-latest

Makefile

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,8 @@ STARTUP_MEM_USAGE = -DSMALL_MEM_STARTUP=0
6868
PRE_POPULATE_PAGES = -DPRE_POPULATE_PAGES=0
6969

7070
## Enable some functionality that like IsoAlloc internals
71-
## for tests that need to verify security properties
71+
## for tests that need to verify security properties.
72+
## Only test code should use this.
7273
UNIT_TESTING = -DUNIT_TESTING=1
7374

7475
## Enable the malloc/free and new/delete hooks
@@ -183,6 +184,15 @@ AUTO_CTOR_DTOR = -DAUTO_CTOR_DTOR=1
183184
## that call free will segfault
184185
ISO_DTOR_CLEANUP = -DISO_DTOR_CLEANUP=0
185186

187+
## Register a signal handler for SIGSEGV that inspects si_addr
188+
## for an address managed by IsoAlloc. Optionally used when
189+
## UAF_PTR_PAGE is enabled for better crash handling
190+
SIGNAL_HANDLER = -DSIGNAL_HANDLER=0
191+
192+
## If you know your target will have an ARMv8.1-A or newer and
193+
## supports Top Byte Ignore (TBI) then you want to enable this
194+
ARM_TBI = 0
195+
186196
LTO = -flto
187197

188198
LIBNAME = libisoalloc.so
@@ -248,8 +258,9 @@ CFLAGS += $(COMMON_CFLAGS) $(SECURITY_FLAGS) $(BUILD_ERROR_FLAGS) $(HOOKS) $(HEA
248258
-std=c11 $(SANITIZER_SUPPORT) $(ALLOC_SANITY) $(MEMCPY_SANITY) $(UNINIT_READ_SANITY) $(CPU_PIN) $(SCHED_GETCPU) \
249259
$(EXPERIMENTAL) $(UAF_PTR_PAGE) $(VERIFY_FREE_BIT_SLOTS) $(NAMED_MAPPINGS) $(ABORT_ON_NULL) $(NO_ZERO_ALLOCATIONS) \
250260
$(ABORT_NO_ENTROPY) $(ISO_DTOR_CLEANUP) $(RANDOMIZE_FREELIST) $(USE_SPINLOCK) $(HUGE_PAGES) $(USE_MLOCK) \
251-
$(MEMORY_TAGGING) $(STRONG_SIZE_ISOLATION) $(MEMSET_SANITY) $(AUTO_CTOR_DTOR)
252-
CXXFLAGS += $(COMMON_CFLAGS) -DCPP_SUPPORT=1 -std=c++17 $(SANITIZER_SUPPORT) $(HOOKS)
261+
$(MEMORY_TAGGING) $(STRONG_SIZE_ISOLATION) $(MEMSET_SANITY) $(AUTO_CTOR_DTOR) $(SIGNAL_HANDLER)
262+
CXXFLAGS = $(COMMON_CFLAGS) -DCPP_SUPPORT=1 -std=c++17 $(SANITIZER_SUPPORT) $(HOOKS)
263+
253264
EXE_CFLAGS = -fPIE
254265
GDB_FLAGS = -g -ggdb3 -fno-omit-frame-pointer
255266
PERF_FLAGS = -pg -DPERF_TEST_BUILD=1
@@ -318,7 +329,7 @@ tests: clean library_debug_unit_tests
318329
@echo "make tests"
319330
$(CC) $(CFLAGS) $(EXE_CFLAGS) $(DEBUG_LOG_FLAGS) $(GDB_FLAGS) $(OS_FLAGS) tests/rand_freelist.c $(ISO_ALLOC_PRINTF_SRC) -o $(BUILD_DIR)/rand_freelist $(LDFLAGS)
320331
$(CC) $(CFLAGS) $(EXE_CFLAGS) $(DEBUG_LOG_FLAGS) $(GDB_FLAGS) $(OS_FLAGS) tests/tests.c $(ISO_ALLOC_PRINTF_SRC) -o $(BUILD_DIR)/tests $(LDFLAGS)
321-
$(CC) $(CFLAGS) $(EXE_CFLAGS) $(DEBUG_LOG_FLAGS) $(GDB_FLAGS) $(OS_FLAGS) tests/uaf.c $(ISO_ALLOC_PRINTF_SRC) -o $(BUILD_DIR)/uaf $(LDFLAGS)
332+
$(CC) $(CFLAGS) $(EXE_CFLAGS) $(DEBUG_LOG_FLAGS) $(GDB_FLAGS) $(OS_FLAGS) $(UNIT_TESTING) tests/uaf.c $(ISO_ALLOC_PRINTF_SRC) -o $(BUILD_DIR)/uaf $(LDFLAGS)
322333
$(CC) $(CFLAGS) $(EXE_CFLAGS) $(DEBUG_LOG_FLAGS) $(GDB_FLAGS) $(OS_FLAGS) tests/interfaces_test.c $(ISO_ALLOC_PRINTF_SRC) -o $(BUILD_DIR)/interfaces_test $(LDFLAGS)
323334
$(CC) $(CFLAGS) $(EXE_CFLAGS) $(DEBUG_LOG_FLAGS) $(GDB_FLAGS) $(OS_FLAGS) tests/thread_tests.c $(ISO_ALLOC_PRINTF_SRC) -o $(BUILD_DIR)/thread_tests $(LDFLAGS)
324335
$(CC) $(CFLAGS) $(EXE_CFLAGS) $(DEBUG_LOG_FLAGS) $(GDB_FLAGS) $(OS_FLAGS) $(UNIT_TESTING) tests/big_canary_test.c $(ISO_ALLOC_PRINTF_SRC) -o $(BUILD_DIR)/big_canary_test $(LDFLAGS)
@@ -400,10 +411,10 @@ install:
400411
cp -pR build/$(LIBNAME) /usr/lib/
401412

402413
format:
403-
clang-format $(SRC_DIR)/*.* tests/*.* include/*.h -i
414+
clang-format-12 $(SRC_DIR)/*.* tests/*.* include/*.h -i
404415

405416
format-ci:
406-
clang-format --Werror --dry-run $(SRC_DIR)/*.* tests/*.* include/*.h -i
417+
clang-format-12 --Werror --dry-run $(SRC_DIR)/*.* tests/*.* include/*.h -i
407418

408419
clean:
409420
rm -rf build/* tests_perf_analysis.txt big_tests_perf_analysis.txt gmon.out test_output.txt *.dSYM core* iso_alloc_profiler.data

include/conf.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@
4545
* magic value that is written */
4646
#if UAF_PTR_PAGE
4747
#define UAF_PTR_PAGE_ODDS 1000000
48-
#define UAF_PTR_PAGE_ADDR 0xFF41414142434445
4948
#endif
5049

5150
/* Zones can be retired after a certain number of

include/iso_alloc_ds.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,9 @@ typedef struct {
102102
#if NO_ZERO_ALLOCATIONS
103103
void *zero_alloc_page;
104104
#endif
105+
#if UAF_PTR_PAGE
106+
void *uaf_ptr_page;
107+
#endif
105108
} __attribute__((aligned(sizeof(int64_t)))) iso_alloc_root;
106109

107110
typedef struct {

include/iso_alloc_internal.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ assert(sizeof(size_t) >= 64)
3838
#include <string.h>
3939
#include <unistd.h>
4040
#include <stdarg.h>
41+
#include <signal.h>
4142
#include <sys/mman.h>
4243
#include <sys/types.h>
4344

@@ -327,10 +328,6 @@ extern pthread_mutex_t root_busy_mutex;
327328
#define UNLOCK_BIG_ZONE()
328329
#endif
329330

330-
#if NO_ZERO_ALLOCATIONS
331-
extern void *_zero_alloc_page;
332-
#endif
333-
334331
/* The global root */
335332
extern iso_alloc_root *_root;
336333

@@ -402,6 +399,10 @@ INTERNAL_HIDDEN int64_t check_canary_no_abort(iso_alloc_zone_t *zone, const void
402399
INTERNAL_HIDDEN void _iso_alloc_initialize(void);
403400
INTERNAL_HIDDEN void _iso_alloc_destroy(void);
404401

402+
#if SIGNAL_HANDLER
403+
INTERNAL_HIDDEN void handle_signal(int sig, siginfo_t *si, void *ctx);
404+
#endif
405+
405406
#if EXPERIMENTAL
406407
INTERNAL_HIDDEN void _iso_alloc_search_stack(uint8_t *stack_start);
407408
#endif

src/iso_alloc.c

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -253,13 +253,28 @@ INTERNAL_HIDDEN void _iso_alloc_initialize(void) {
253253
_root->zero_alloc_page = mmap_pages(g_page_size, false, NULL, PROT_NONE);
254254
#endif
255255

256+
#if UAF_PTR_PAGE
257+
_root->uaf_ptr_page = mmap_pages(g_page_size, false, NULL, PROT_NONE);
258+
#endif
259+
256260
#if ALLOC_SANITY && UNINIT_READ_SANITY
257261
_iso_alloc_setup_userfaultfd();
258262
#endif
259263

260264
#if ALLOC_SANITY
261265
_sanity_canary = us_rand_uint64(&_root->seed);
262266
#endif
267+
268+
#if SIGNAL_HANDLER
269+
struct sigaction sa;
270+
sigemptyset(&sa.sa_mask);
271+
sa.sa_flags = SA_NODEFER | SA_SIGINFO;
272+
sa.sa_sigaction = &handle_signal;
273+
274+
if(sigaction(SIGSEGV, &sa, NULL) == ERR) {
275+
LOG("Could not register signal handler");
276+
}
277+
#endif
263278
}
264279

265280
#if AUTO_CTOR_DTOR
@@ -1386,7 +1401,7 @@ INTERNAL_HIDDEN void iso_free_chunk_from_zone(iso_alloc_zone_t *zone, void *rest
13861401
LOG_AND_ABORT("Chunk at 0x%p of zone[%d] is not %d byte aligned", p, zone->index, ALIGNMENT);
13871402
}
13881403

1389-
const uint64_t chunk_offset = (uint64_t)(p - UNMASK_USER_PTR(zone));
1404+
const uint64_t chunk_offset = (uint64_t) (p - UNMASK_USER_PTR(zone));
13901405
const size_t chunk_number = (chunk_offset >> zone->chunk_size_pow2);
13911406
const bit_slot_t bit_slot = (chunk_number << BITS_PER_CHUNK_SHIFT);
13921407
const bit_slot_t dwords_to_bit_slot = (bit_slot >> BITS_PER_QWORD_SHIFT);
@@ -1654,7 +1669,7 @@ INTERNAL_HIDDEN iso_alloc_zone_t *_iso_free_internal_unlocked(void *p, bool perm
16541669
/* We only need to refresh this single tag */
16551670
void *user_pages_start = UNMASK_USER_PTR(zone);
16561671
uint8_t *_mtp = (user_pages_start - g_page_size - ROUND_UP_PAGE(zone->chunk_count * MEM_TAG_SIZE));
1657-
uint64_t chunk_offset = (uint64_t)(p - user_pages_start);
1672+
uint64_t chunk_offset = (uint64_t) (p - user_pages_start);
16581673
_mtp += (chunk_offset >> zone->chunk_size_pow2);
16591674

16601675
/* Generate and write a new tag for this chunk */
@@ -1909,6 +1924,10 @@ INTERNAL_HIDDEN void _iso_alloc_destroy(void) {
19091924
munmap(_root->zero_alloc_page, g_page_size);
19101925
#endif
19111926

1927+
#if UAF_PTR_PAGE
1928+
munmap(_root->uaf_ptr_page, g_page_size);
1929+
#endif
1930+
19121931
#if DEBUG && (LEAK_DETECTOR || MEM_USAGE)
19131932
#if MEM_USAGE
19141933
uint64_t mb = 0;

src/iso_alloc_mem_tags.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ INTERNAL_HIDDEN uint8_t _iso_alloc_get_mem_tag(void *p, iso_alloc_zone_t *zone)
1414
}
1515

1616
uint8_t *_mtp = (user_pages_start - g_page_size - ROUND_UP_PAGE(zone->chunk_count * MEM_TAG_SIZE));
17-
const uint64_t chunk_offset = (uint64_t)(p - user_pages_start);
17+
const uint64_t chunk_offset = (uint64_t) (p - user_pages_start);
1818

1919
/* Ensure the pointer is a multiple of chunk size */
2020
if(UNLIKELY((chunk_offset & (zone->chunk_size - 1)) != 0)) {

src/iso_alloc_profiler.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ INTERNAL_HIDDEN uint64_t _iso_alloc_zone_leak_detector(iso_alloc_zone_t *zone, b
160160

161161
if(profile == false) {
162162
LOG("Zone[%d] Total number of %d byte chunks(%d) used and free'd (%d) (%d percent), in use = %d", zone->index, zone->chunk_size, zone->chunk_count,
163-
was_used, (int32_t)((float) was_used / zone->chunk_count) * 100, in_use);
163+
was_used, (int32_t) ((float) was_used / zone->chunk_count) * 100, in_use);
164164
}
165165

166166
MASK_ZONE_PTRS(zone);
@@ -172,7 +172,7 @@ INTERNAL_HIDDEN uint64_t _iso_alloc_zone_leak_detector(iso_alloc_zone_t *zone, b
172172
* in use and previously used by this zone */
173173
if(profile == true) {
174174
uint64_t total = (in_use + was_used);
175-
return (uint64_t)((float) (total / zone->chunk_count) * 100.0);
175+
return (uint64_t) ((float) (total / zone->chunk_count) * 100.0);
176176
}
177177
#endif
178178
return in_use;
@@ -395,7 +395,7 @@ INTERNAL_HIDDEN void _iso_alloc_profile(size_t size) {
395395
used = _iso_alloc_zone_leak_detector(zone, true);
396396
}
397397

398-
used = (int32_t)((float) (used / zone->chunk_count) * 100.0);
398+
used = (int32_t) ((float) (used / zone->chunk_count) * 100.0);
399399

400400
if(used > CHUNK_USAGE_THRESHOLD) {
401401
_zone_profiler_map[zone->chunk_size].count++;

src/iso_alloc_search.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ INTERNAL_HIDDEN void *_iso_alloc_ptr_search(void *n, bool poison) {
2525
return search;
2626
} else {
2727
#if UAF_PTR_PAGE
28-
*(uint64_t *) search = UAF_PTR_PAGE_ADDR;
28+
*(uint64_t *) search = (uint64_t) (_root->uaf_ptr_page);
2929
return search;
3030
#endif
3131
}
@@ -82,7 +82,7 @@ INTERNAL_HIDDEN void _iso_alloc_search_stack(uint8_t *stack_start) {
8282
LOG_AND_ABORT("Chunk at 0x%p of zone[%d] is not %d byte aligned", p, zone->index, ALIGNMENT);
8383
}
8484

85-
uint64_t chunk_offset = (uint64_t)(p - (uint64_t *) zone->user_pages_start);
85+
uint64_t chunk_offset = (uint64_t) (p - (uint64_t *) zone->user_pages_start);
8686
LOG("zone[%d] user_pages_start=%p value=%p %lu %d", zone->index, zone->user_pages_start, p, chunk_offset, zone->chunk_size);
8787

8888
/* Ensure the pointer is a multiple of chunk size */

src/iso_alloc_signal.c

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/* iso_alloc_signal.c - A secure memory allocator
2+
* Copyright 2022 - chris.rohlf@gmail.com */
3+
4+
#include "iso_alloc_internal.h"
5+
6+
#if SIGNAL_HANDLER
7+
INTERNAL_HIDDEN void handle_signal(int sig, siginfo_t *si, void *ctx) {
8+
void *crash_addr = si->si_addr;
9+
10+
#if UAF_PTR_PAGE
11+
if(si->si_addr == NULL) {
12+
LOG_AND_ABORT("si->si_addr == NULL");
13+
}
14+
15+
/* Check for the address within 2 pages in
16+
* either direction of _root->uaf_ptr_page */
17+
if(crash_addr >= _root->uaf_ptr_page - (g_page_size * 2) &&
18+
crash_addr <= _root->uaf_ptr_page + (g_page_size * 2)) {
19+
LOG_AND_ABORT("Use after free detected! Crashed at _root->uaf_ptr_page 0x%x", si->si_addr);
20+
}
21+
#endif
22+
23+
LOG_AND_ABORT("Unknown segmentation fault @ 0x%p", crash_addr);
24+
}
25+
#endif

0 commit comments

Comments
 (0)