Rust Memory Sanitizer: Safe Allocation and Deallocation
Memory safety is a critical concern in software development. This challenge asks you to implement a simplified memory sanitizer in Rust that tracks allocations and deallocations, ensuring that all allocated memory is eventually freed. This exercise will reinforce your understanding of Rust's ownership and borrowing system, and introduce concepts related to memory management and debugging.
Problem Description
You are tasked with creating a MemorySanitizer struct that manages a pool of allocated memory blocks. The sanitizer should provide functions for allocating memory, deallocating memory, and reporting any memory leaks at the end of the program. The core functionality revolves around tracking allocated blocks and ensuring they are freed.
What needs to be achieved:
- Implement a
MemorySanitizerstruct with methods for allocation (allocate), deallocation (deallocate), and leak detection (report_leaks). - The
allocatemethod should return a pointer to a newly allocated block of memory. - The
deallocatemethod should free a previously allocated block of memory. - The
report_leaksmethod should iterate through the tracked allocations and report any blocks that have not been deallocated.
Key Requirements:
- The
MemorySanitizershould internally maintain a data structure (e.g., aVec) to track allocated memory blocks. Each entry in this structure should store the pointer to the allocated block and its size. - The
allocatemethod should allocate memory usingBox::newand store the pointer and size in the internal tracking structure. - The
deallocatemethod should remove the corresponding entry from the tracking structure and effectively drop theBoxto free the memory. - The
report_leaksmethod should print a message for each leaked block, including its address and size. - The sanitizer should be thread-safe. Use
Mutexto protect the internal data structure.
Expected Behavior:
allocateshould return a valid pointer to allocated memory.deallocateshould successfully free the memory pointed to by the provided pointer.report_leaksshould accurately identify and report any memory leaks.- Multiple allocations and deallocations should work correctly.
- Deallocating the same pointer twice should panic (as
Box::dropdoes).
Edge Cases to Consider:
- Allocation of zero-sized blocks (should be handled gracefully).
- Deallocation of a pointer that was not allocated by the sanitizer.
- Concurrent allocation and deallocation from multiple threads.
- Deallocation of a pointer that has already been deallocated.
Examples
Example 1:
Input:
allocate(10);
allocate(20);
deallocate(ptr1);
report_leaks();
Output:
Leaked memory block at address: 0x..., size: 20
Explanation: The first allocation creates a block of 10 bytes. The second creates a block of 20 bytes. The first block is deallocated, leaving the second block as a leak.
Example 2:
Input:
allocate(5);
deallocate(ptr1);
deallocate(ptr1); // Attempt to deallocate twice
report_leaks();
Output:
thread 'main' panicked at 'called `drop` twice on the same data', src/main.rs:XX:XX
Explanation: The first allocation creates a block of 5 bytes. The first deallocation frees the memory. The second deallocation attempts to free the same memory again, causing a panic.
Example 3: (Concurrent Access)
Input: (Multiple threads concurrently allocating and deallocating)
report_leaks();
Output: (May vary depending on allocation/deallocation order, but should accurately report leaks)
Leaked memory block at address: 0x..., size: ...
Explanation: Concurrent access is handled safely by the Mutex. The final report_leaks call should accurately identify any memory blocks that were allocated but not deallocated, regardless of the order of operations.
Constraints
- The
allocatefunction should be able to allocate blocks of up to 1024 bytes. - The
deallocatefunction should only accept pointers that were previously allocated by theallocatefunction. - The
report_leaksfunction should complete within 100 milliseconds. - The sanitizer must be thread-safe, handling concurrent access from multiple threads without data races.
- The solution must be written in Rust and compile without warnings.
Notes
- Consider using a
Vec<(usize, *mut u8)>to store the allocated blocks, whereusizerepresents the size of the block and*mut u8is the pointer to the block. - The
allocatefunction should return the pointer to the allocated memory. - The
deallocatefunction should take the pointer as input. - Use
std::sync::Mutexto protect the internal data structure from concurrent access. - Error handling is not required for this challenge; assume that allocation always succeeds. Focus on the core memory tracking and leak detection logic.
- You can use
std::ptr::null_mut()as a placeholder for an invalid pointer. - The addresses in the
report_leaksoutput are for demonstration purposes and may not be meaningful in a real-world scenario.