aboutsummaryrefslogtreecommitdiffstats
path: root/libtest/libtest_alloc.c
diff options
context:
space:
mode:
authorMattias Andrée <m@maandree.se>2026-05-08 22:29:35 +0200
committerMattias Andrée <m@maandree.se>2026-05-08 22:29:35 +0200
commit2d3a573977417d917c16742d8d9d8ead047d0ebc (patch)
treecaeac52856a9df0478e2bee53e5dda1f84422461 /libtest/libtest_alloc.c
parentAdd DEFAULT_SUPPORT option and improve DEPENDENCIES (diff)
downloadlibrecrypt-2d3a573977417d917c16742d8d9d8ead047d0ebc.tar.gz
librecrypt-2d3a573977417d917c16742d8d9d8ead047d0ebc.tar.bz2
librecrypt-2d3a573977417d917c16742d8d9d8ead047d0ebc.tar.xz
Misc
Signed-off-by: Mattias Andrée <m@maandree.se>
Diffstat (limited to '')
-rw-r--r--libtest/libtest_alloc.c208
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