Files
2025-12-02 13:35:30 +00:00
..
2025-12-02 13:35:30 +00:00

Benchmarks for ERTS 2026: Fuzz Testing in Memory-Safe Verification Environments

This directory contains a suite of benchmark tests designed to evaluate the effectiveness of fuzz testing in detecting memory safety vulnerabilities when starting from Ada entry points. These benchmarks are part of a study presented at the 2026 Embedded Real Time Systems Conference.

Overview

Each benchmark consists of an Ada subprogram that serves as a fuzzing entry point, which then calls into C code containing deliberate memory safety vulnerabilities. The goal is to determine whether a fuzzer can discover the specific input values that trigger these vulnerabilities by fuzzing the Ada interface.

Test Classification

The benchmarks are organized according to the following vulnerability categories:

Bug Type Number of Test Variants Description
Stack buffer overflow 3 A stack buffer overflow occurs when a program writes more data than a stack-allocated buffer can hold.
Heap buffer overflow 2 A heap buffer overflow happens when writes extend past the bounds of a dynamically allocated buffer.
Double free 2 Occurs when a program calls free() (or equivalent) more than once on the same memory pointer.
Use after free 2 Occurs when a program continues to use or reference memory after it has been freed/deallocated.
Array in struct 2 Occurs when doing an out of bounds access in an array declared within a struct.
Out of bounds pointer arithmetics 1 Vulnerability where out-of-bounds pointer arithmetic causes a pointer to jump from one buffer into an adjacent buffer illegally.
ASan overflow evading 1 A specific heap buffer overflow that exploits AddressSanitizer's fundamental architectural limitation by overflowing a buffer such that ASan poisoned redzone memory is avoided.

Benchmark Test Details

Stack Buffer Overflow (3 variants)

1. simple_bugs.c::simple_stack_overflow()

  • Entry Point: Called from Ada via FFI binding
  • Goal: Detect stack buffer overflow by fuzzing the value parameter
  • Vulnerability: The function creates three stack-allocated buffers and writes beyond the bounds of the middle buffer both before (negative indexing) and after (positive indexing) its allocated space
  • Trigger Condition: When value is large enough to cause the loop indices to access memory outside the middle[STACK_SIZE] array bounds

2. bugs.c::process_user_config()

  • Entry Point: Called from Ada via FFI binding
  • Goal: Detect complex stack buffer overflow requiring multiple conditions
  • Vulnerability: Stack buffer of size 16 can be overflowed via memcpy() with attacker-controlled length
  • Trigger Condition: Requires length > 16 AND length < 100 AND config_id in range [800001, 800999]

3. ada_bugs.adb::Buffer_Overflow_Fuzz()

  • Entry Point: Pure Ada subprogram Ada_Bugs.Buffer_Overflow_Fuzz(Index: Integer)
  • Goal: Detect buffer overflow using unchecked type conversion
  • Vulnerability: A 10-element buffer is unsafely cast to a 50-element buffer pointer using Ada.Unchecked_Conversion, allowing writes beyond the original buffer bounds
  • Trigger Condition: When Index > 10 (the original buffer size)

Heap Buffer Overflow (2 variants)

1. simple_bugs.c::simple_heap_buffer_overflow()

  • Entry Point: Called from Ada via FFI binding
  • Goal: Detect heap buffer overflow (off-by-one error)
  • Vulnerability: A heap buffer of size value is allocated, but the read loop iterates to i <= value instead of i < value, causing a one-byte read overflow
  • Trigger Condition: Any non-zero value will trigger the off-by-one read

2. bugs.c::write_cache_entry()

  • Entry Point: Called from Ada via FFI binding
  • Goal: Detect heap out-of-bounds write with incorrect bounds checking
  • Vulnerability: A heap array of 10 elements is allocated, but a second conditional branch allows writes at extremely large offsets without proper bounds checking
  • Trigger Condition: Requires offset in range [500000, 599999] to reach the vulnerable code path

Double Free (2 variants)

1. bugs.c::cleanup_resource_handle()

  • Entry Point: Called from Ada via FFI binding
  • Goal: Detect double free caused by overlapping cleanup logic
  • Vulnerability: Memory is freed by "Module A" if cleanup_id is in [100001, 199999], then freed again by "Module B" if cleanup_id is in [150001, 249999]
  • Trigger Condition: Requires cleanup_id in the overlapping range [150001, 199999]

2. ada_bugs.adb::Double_Free_Fuzz()

  • Entry Point: Pure Ada subprogram Ada_Bugs.Double_Free_Fuzz(Free_Count: Integer)
  • Goal: Detect double free using unchecked deallocation
  • Vulnerability: Memory is freed when Free_Count >= 1, then the same pointer is reconstituted from a saved address and freed again when Free_Count >= 2
  • Trigger Condition: When Free_Count >= 2

Use After Free (2 variants)

1. bugs.c::handle_session_data()

  • Entry Point: Called from Ada via FFI binding
  • Goal: Detect use-after-free requiring specific input range
  • Vulnerability: Memory is freed when action_code is in [1000000, 1001000], but subsequently accessed regardless of whether it was freed
  • Trigger Condition: Requires action_code in range [1000000, 1001000]

2. ada_bugs.adb::Use_After_Free_Fuzz()

  • Entry Point: Pure Ada subprogram Ada_Bugs.Use_After_Free_Fuzz(Control: Integer)
  • Goal: Detect use-after-free using unchecked pointer manipulation
  • Vulnerability: A pointer is freed when Control < 0, but its address is saved and the pointer is reconstituted and dereferenced regardless
  • Trigger Condition: When Control < 0

Array in Struct (2 variants)

1. simple_bugs.c::simple_struct_array_access()

  • Entry Point: Called from Ada via FFI binding
  • Goal: Detect out-of-bounds access to an array within a struct
  • Vulnerability: A struct contains a 4-byte array followed by an unsigned int. The array is accessed with index % sizeof(super_struct), which can access beyond the array into the other_value field or beyond
  • Trigger Condition: When index modulo the struct size results in an index >= 4 (the array size)

2. ada_bugs.adb::Record_Array_Access()

  • Entry Point: Pure Ada subprogram Ada_Bugs.Record_Array_Access(Index: Interfaces.Unsigned_8)
  • Goal: Detect out-of-bounds access to an array field within an Ada record
  • Vulnerability: A record contains a 128-element array followed by an unsigned 32-bit integer. Array access uses the full range of an unsigned 8-bit index (0-255), allowing writes beyond the array bounds
  • Trigger Condition: When Index > 128

Out of Bounds Pointer Arithmetics (1 variant)

out_of_bounds_arithmetic.c::test_out_of_bounds_arithmetic()

  • Entry Point: Called from Ada via Bounds_Test.Execute_Test(Offset_Multiplier: Integer)
  • Goal: Demonstrate vulnerability where out-of-bounds pointer arithmetic causes a pointer to illegally jump from one buffer to an adjacent buffer
  • Vulnerability: Two adjacent buffers are allocated on the stack. A pointer to the first buffer is advanced by the distance between the buffers, causing it to point into the second buffer. This is a capability violation that ASAN cannot detect (it only checks dereferences, not arithmetic)
  • Trigger Condition: Any input value that doesn't crash the program will trigger the vulnerability
  • Note: This is primarily designed to demonstrate the difference between ASAN and CHERI-style capability-based protection

ASan Overflow Evading (1 variant)

asan_evasion.c::test_asan_evade()

  • Entry Point: Called from Ada via FFI binding
  • Goal: Demonstrate a heap buffer overflow that can evade AddressSanitizer's detection
  • Vulnerability: Two small (1-byte) heap buffers are allocated adjacently. An overflow of 31 bytes from the first buffer attempts to corrupt the second buffer. Due to ASAN's redzone architecture (typically 16-32 bytes between allocations), this specific overflow distance can potentially avoid the poisoned redzone and reach the second buffer undetected
  • Trigger Condition: Requires heap layout where buffers are positioned such that a 32-byte overflow from buf1[0] reaches buf2[0] without hitting ASAN redzones
  • Note: This is an architectural limitation of ASAN's redzone-based approach

Source Files

Ada Entry Points

  • ada_bugs.ads/ada_bugs.adb: Pure Ada implementations of buffer overflow, use-after-free, double-free, and record array access vulnerabilities
  • bounds_test.ads/bounds_test.adb: Ada wrapper for the out-of-bounds pointer arithmetic test
  • test_bounds_ada.adb: Standalone Ada program demonstrating the bounds test
  • bugs_c.ads: Ada FFI bindings for complex C vulnerabilities in bugs.c
  • simple_bugs_c.ads: Ada FFI bindings for simpler C vulnerabilities in simple_bugs.c

C Implementations

  • bugs.c: Complex vulnerabilities requiring specific input ranges (stack overflow, heap overflow, double free, use-after-free, uninitialized memory, null pointer dereference)
  • simple_bugs.c: Simpler vulnerabilities with fewer preconditions (stack overflow, heap overflow, struct array access)
  • out_of_bounds_arithmetic.c: Pointer arithmetic vulnerability demonstrating ASAN limitations
  • asan_evasion.c: Heap overflow specifically designed to evade ASAN detection

Usage

These benchmarks are designed to be compiled with instrumentation (ASAN, MSAN, UBSan, etc.) and fuzzed from their Ada entry points. The fuzzer must discover the specific input values that satisfy the conditional branches leading to each vulnerability.

Research Context

This benchmark suite supports research into:

  1. The effectiveness of fuzz testing when starting from memory-safe languages (Ada)
  2. The ability of fuzzers to discover complex, condition-guarded vulnerabilities
  3. The detection capabilities and limitations of different sanitizers (ASAN, MSAN, CHERI)
  4. Cross-language vulnerability propagation in mixed Ada/C codebases

License

Part of the ERTS 2026 conference submission materials.