diff options
| author | Mattias Andrée <m@maandree.se> | 2026-05-08 22:29:35 +0200 |
|---|---|---|
| committer | Mattias Andrée <m@maandree.se> | 2026-05-08 22:29:35 +0200 |
| commit | 2d3a573977417d917c16742d8d9d8ead047d0ebc (patch) | |
| tree | caeac52856a9df0478e2bee53e5dda1f84422461 /libtest/libtest_alloc.c | |
| parent | Add DEFAULT_SUPPORT option and improve DEPENDENCIES (diff) | |
| download | librecrypt-2d3a573977417d917c16742d8d9d8ead047d0ebc.tar.gz librecrypt-2d3a573977417d917c16742d8d9d8ead047d0ebc.tar.bz2 librecrypt-2d3a573977417d917c16742d8d9d8ead047d0ebc.tar.xz | |
Misc
Signed-off-by: Mattias Andrée <m@maandree.se>
Diffstat (limited to 'libtest/libtest_alloc.c')
| -rw-r--r-- | libtest/libtest_alloc.c | 208 |
1 files changed, 208 insertions, 0 deletions
diff --git a/libtest/libtest_alloc.c b/libtest/libtest_alloc.c new file mode 100644 index 0000000..5aa3218 --- /dev/null +++ b/libtest/libtest_alloc.c @@ -0,0 +1,208 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +#if defined(WITH_BACKTRACE) && defined(__GNUC__) +# pragma GCC diagnostic ignored "-Walloca" +#endif + + +static _Thread_local int inside_mmap_anon = 0; + + +static void +meminfo_fixup(struct meminfo *meminfo) +{ + if (meminfo->alignment_type == DEFAULT_ALIGNMENT) + meminfo->requested_alignment = _Alignof(max_align_t); + else if (meminfo->alignment_type == PAGE_ALIGNMENT) + meminfo->requested_alignment = libtest_get_pagesize(); + + meminfo->actual_alignment = meminfo->requested_alignment; + meminfo->accept_leakage = !!libtest_malloc_accept_leakage; + + if (libtest_zero_on_alloc) + meminfo->initialised_on_alloc |= !meminfo->accept_leakage; +} + + +static int +add_or_enomem(size_t *augend, size_t augment) +{ + if (*augend > SIZE_MAX - augment) { + errno = ENOMEM; + return -1; + } + *augend += augment; + return 0; +} + + +static void * +mmap_anon(size_t size) +{ + void *ptr; + inside_mmap_anon = 1; + ptr = libtest_real_mmap(NULL, size, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS | MAP_UNINITIALIZED, + -1, 0); + inside_mmap_anon = 0; + return ptr == MAP_FAILED ? NULL : ptr; +} + + +void * +libtest_alloc(struct meminfo *meminfo) +{ + static _Thread_local int recursion_guard = 0; + void *base_ptr, *ret_ptr; + size_t misalignment; + size_t bookkeeping; + int saved_errno; +#ifdef WITH_BACKTRACE + size_t backtrace_realignment; + size_t i, backtrace_n; + unw_cursor_t cursor; + unw_context_t context; + unw_word_t rip; + struct partial { + uintptr_t rips[8]; + struct partial *next; + } *current, head; +#endif + + assert(!inside_mmap_anon); + + saved_errno = errno; + meminfo_fixup(meminfo); +#ifdef WITH_BACKTRACE + meminfo->accept_leakage |= libtest_malloc_internal_usage > 0; +#endif + + /* Get backtrace (have to do it now to calculate `backtrace_n` for allocation) */ +#ifdef WITH_BACKTRACE + backtrace_n = 0u; + if (!libtest_malloc_accept_leakage && !libtest_malloc_internal_usage) { + libtest_malloc_internal_usage++; + if (unw_getcontext(&context) || unw_init_local(&cursor, &context)) + goto skip_backtrace; + for (current = &head, i = 0u; unw_step(&cursor) > 0; i++) { + if (i % ELEMSOF(current->rips) == 0u) + current = current->next = alloca(sizeof(*current)); + if (unw_get_reg(&cursor, UNW_REG_IP, &rip)) + goto skip_backtrace; + current->rips[i % ELEMSOF(current->rips)] = (uintptr_t)rip; + } + backtrace_n = i; + skip_backtrace: + libtest_malloc_internal_usage--; + } +#endif + + /* Calculate required alloction size */ + meminfo->real_alloc_size = meminfo->requested_alloc_size; + if (add_or_enomem(&meminfo->real_alloc_size, sizeof(struct meminfo)) || + add_or_enomem(&meminfo->real_alloc_size, _Alignof(void *) + sizeof(void *)) || + add_or_enomem(&meminfo->real_alloc_size, meminfo->actual_alignment)) + return NULL; +#ifdef WITH_BACKTRACE + if (backtrace_n) { + misalignment = sizeof(struct meminfo) % _Alignof(struct backtrace); + backtrace_realignment = misalignment ? _Alignof(struct backtrace) - misalignment : 0u; + if (add_or_enomem(&meminfo->real_alloc_size, backtrace_realignment) || + add_or_enomem(&meminfo->real_alloc_size, offsetof(struct backtrace, trace)) || + add_or_enomem(&meminfo->real_alloc_size, backtrace_n * sizeof(Dwarf_Addr))) + return NULL; + } +#endif + bookkeeping = meminfo->real_alloc_size - meminfo->requested_alloc_size; + bookkeeping -= meminfo->actual_alignment; + + /* Allocate memory */ + base_ptr = mmap_anon(meminfo->real_alloc_size); + if (!base_ptr) + return NULL; + if (meminfo->initialised_on_alloc) + memset(base_ptr, 0, meminfo->real_alloc_size); + ret_ptr = &((char *)base_ptr)[bookkeeping]; + + /* Save backtrace */ +#ifdef WITH_BACKTRACE + if (backtrace_n) { + meminfo->backtrace = (void *)&((char *)base_ptr)[sizeof(struct meminfo) + backtrace_realignment]; + meminfo->backtrace->n = backtrace_n; + for (current = &head, i = 0u; i < backtrace_n; i++) { + if (i % ELEMSOF(current->rips) == 0u) + current = current->next; + meminfo->backtrace->trace[i] = current->rips[i % ELEMSOF(current->rips)]; + } + } else { + meminfo->backtrace = NULL; + } +#endif + + /* Fix alignment */ + misalignment = (size_t)(uintptr_t)ret_ptr % meminfo->actual_alignment; + if (misalignment) + ret_ptr = &((char *)ret_ptr)[meminfo->actual_alignment - misalignment]; + + /* Store usable size */ + meminfo->usable_alloc_size = meminfo->real_alloc_size; + meminfo->usable_alloc_size -= (size_t)((char *)ret_ptr - (char *)base_ptr); + + /* Store book-keeping */ + meminfo->usable_area = ret_ptr; + memcpy(base_ptr, meminfo, sizeof(*meminfo)); + BASE_POINTER(ret_ptr) = base_ptr; + meminfo = base_ptr; + + /* Track allocation */ + if (!libtest_kill_malloc_tracking) { + assert(!recursion_guard); + recursion_guard = 1; + SPINLOCK(libtest_allocs_list_spinlock); + if (!libtest_allocs_list_inited) { + libtest_allocs_head.prev = NULL; + libtest_allocs_head.next = &libtest_allocs_tail; + libtest_allocs_tail.prev = &libtest_allocs_head; + libtest_allocs_tail.next = NULL; + libtest_allocs_list_inited = 1; + } + libtest_allocs_tail.prev->next = meminfo; + meminfo->prev = libtest_allocs_tail.prev; + meminfo->next = &libtest_allocs_tail; + SPINUNLOCK(libtest_allocs_list_spinlock); + recursion_guard = 0; + } + + /* Optionally print out trace */ +#ifdef WITH_BACKTRACE + if (meminfo->backtrace && !libtest_malloc_internal_usage && getenv("TRACE_MALLOC")) { + libtest_malloc_internal_usage++; + fprintf(stderr, "Memory allocated: %p (alloc-size=%zu, real-size=%zu, leak-allowed=%i)\n", + ret_ptr, meminfo->requested_alloc_size, meminfo->real_alloc_size, meminfo->accept_leakage); + libtest_print_backtrace(stderr, "\tat ", 0u, NULL); + fflush(stderr); + libtest_malloc_internal_usage--; + } +#endif + + errno = saved_errno; + return ret_ptr; +} + + +#else + + +CONST int +main(void) +{ + /* Tested via alloc.c */ + return 0; +} + + +#endif |
