Hone logo
Hone
Problems

Implementing a Basic Address Sanitizer in Rust

Address Sanitizer (ASan) is a memory error detector. It helps identify common memory safety issues like use-after-free, heap-buffer-overflow, stack-buffer-overflow, and more. This challenge asks you to implement a simplified version of ASan in Rust, focusing on detecting heap-buffer-overflows and use-after-free errors. This exercise will deepen your understanding of memory management and Rust's safety features, while also providing insight into how ASan works under the hood.

Problem Description

You are tasked with creating a rudimentary Address Sanitizer (ASan) implementation in Rust. The core functionality should focus on detecting heap-buffer-overflows and use-after-free errors when dealing with Vec<u8> (byte vectors) allocated on the heap. Your ASan implementation will wrap the Vec<u8> type, adding metadata and checks to track allocations and deallocations.

Specifically, your ASan implementation should:

  1. Track Allocations: Maintain a data structure (e.g., a HashMap) to store information about each allocated Vec<u8>, including its address and size.
  2. Detect Heap-Buffer-Overflows: When a Vec<u8> is accessed (read or write), verify that the index is within the bounds of the allocated memory. If an out-of-bounds access is detected, report an error with the address and index.
  3. Detect Use-After-Free: When a Vec<u8> is deallocated, mark it as freed in the allocation tracking data structure. Subsequent accesses to the deallocated Vec<u8> should trigger a use-after-free error.
  4. Provide a Wrapper Type: Create a new type, ASanVec<T>, that wraps a Vec<T> (in this case, Vec<u8>). All operations on the wrapped Vec<T> should be intercepted and checked by your ASan implementation.

Examples

Example 1:

Input:
```rust
#[derive(Debug)]
struct ASanVec<T> {
    vec: Vec<T>,
    // ... (allocation tracking data structure)
}

fn main() {
    let mut asan_vec: ASanVec<u8> = ASanVec { vec: vec![1, 2, 3] };

    asan_vec.vec[0] = 4; // Valid access
    println!("{:?}", asan_vec.vec);
}

Output:

[4, 2, 3]

Explanation: A valid access within the bounds of the vector. No error is reported.

Example 2:

Input:
```rust
#[derive(Debug)]
struct ASanVec<T> {
    vec: Vec<T>,
    // ... (allocation tracking data structure)
}

fn main() {
    let mut asan_vec: ASanVec<u8> = ASanVec { vec: vec![1, 2, 3] };

    asan_vec.vec[5] = 4; // Out-of-bounds access
}

Output:

Heap-buffer-overflow detected! Address: [some address], Index: 5, Size: 3

Explanation: An out-of-bounds write is attempted. The ASan implementation detects the heap-buffer-overflow and reports an error.

Example 3:

Input:
```rust
#[derive(Debug)]
struct ASanVec<T> {
    vec: Vec<T>,
    // ... (allocation tracking data structure)
}

fn main() {
    let mut asan_vec: ASanVec<u8> = ASanVec { vec: vec![1, 2, 3] };
    let _ = asan_vec.vec; // Access the vector
    drop(asan_vec); // Deallocate the vector

    asan_vec.vec[0] = 4; // Use-after-free access
}

Output:

Use-after-free detected! Address: [some address]

Explanation: The vector is deallocated, but a subsequent access is attempted. The ASan implementation detects the use-after-free and reports an error.

Constraints

  • The implementation should focus on Vec<u8> for simplicity.
  • The allocation tracking data structure should be efficient enough to handle a reasonable number of allocations (e.g., up to 1000).
  • Error reporting should include the address of the memory being accessed and the index involved in the error. The address can be a placeholder (e.g., a pointer value).
  • Performance should be considered, but correctness is the primary goal. Avoid excessive overhead that would make the ASan implementation unusable.
  • The solution should be self-contained and runnable.

Notes

  • You'll need to implement a mechanism to track the addresses of allocated Vec<u8> instances. Consider using std::ptr::NonNull to represent raw pointers.
  • The Vec<u8>'s get and set methods can be overridden or wrapped to perform the necessary checks. Alternatively, you can implement custom accessors.
  • Consider using a HashMap to store allocation metadata. The key could be the pointer to the allocated memory, and the value could contain the size of the allocation.
  • This is a simplified ASan implementation. A real ASan would involve more sophisticated techniques like shadow memory and instrumentation. This challenge focuses on the core concepts of allocation tracking and error detection.
  • You don't need to handle all possible memory errors, just heap-buffer-overflows and use-after-free errors for Vec<u8>.
  • The address reported in the error messages can be a placeholder value; the exact address is not critical for this exercise.
Loading editor...
rust