Commit c615edb
committed
Add corruption-detection test for probabilistic mitigations
Adds a new functional test, func/corruption_detection, that
validates snmalloc's probabilistic memory-safety mitigations
actually fire on the corruption patterns they are designed to
catch. Without it, regressions that silently weakened a mitigation
would be invisible to the existing suite, since every other test
exercises only the non-failing arm of the integrity checks.
Each scenario runs in a forked child so the expected abort does not
kill the harness. Detection is reported as the child being killed
by SIGABRT/SIGSEGV/SIGBUS/SIGILL; a clean exit means the corruption
went undetected and the test fails.
Six scenarios are covered, spanning the local-thread, remote-thread
and large-allocation paths:
* double_free - small alloc, two local frees of the same
slot. Detected by freelist_backward_edge
when the resulting cycle is later
traversed.
* uaf_freelist - small alloc, free, then write garbage
into the freed slot's first two words
(the obfuscated next/prev). Detected by
check_prev on the next freelist
consumption.
* oob_into_neighbor - tiny allocs, free even slots, overrun
from an odd live slot into freed
neighbours. Detected by check_prev when
the neighbour is later allocated.
* remote_double_free - small alloc, free locally, then free
again from a different thread (the
second free travels via the remote
message queue). Detected as
!meta->is_unused() in the dealloc path.
* remote_uaf - small alloc, free via a different
thread, then write garbage through the
dangling pointer while the slot sits on
the owning allocator's pending-remote
queue. Detected by check_prev during
handle_message_queue_slow's drain - a
code path no other test exercises.
* large_double_free - allocation larger than any small
sizeclass (handled by the chunk
allocator and per-chunk metadata rather
than the slab freelist), freed twice.
Detected as !meta->is_unused() in the
large-dealloc path.
The test is Linux-only (uses fork()/waitpid()) and is a no-op when
SNMALLOC_CHECK_CLIENT is not defined, since the mitigations it
relies on are then compiled out.
The test is also instrumented to cooperate with clang source-based
coverage: the forked child re-resolves LLVM_PROFILE_FILE with its
own pid (the parent's %p expansion is otherwise inherited and all
children would write to the same file) and a signal handler flushes
.profraw before re-raising the fatal signal. The runtime entry
points are declared as weak symbols so the test still links in
non-coverage builds.
Picked up automatically by make_tests so it runs as both
func-corruption_detection-fast and func-corruption_detection-check;
the fast variant immediately exits with the "skip" message because
the mitigations are off.1 parent 4e08765 commit c615edb
1 file changed
Lines changed: 401 additions & 0 deletions
0 commit comments