crabgrind 0.2.5

Rust bindings to "Valgrind Client Request" interface
Documentation
Manipulates the accessibility and validity state of a memory range.

This wrapper injects client requests directly into the execution stream to
modify Memcheck's shadow memory. It is useful for custom allocators or
optimizing instrumentation of complex memory operations.

# Example

Enforcing bounds in a custom allocator:

For custom memory management (e.g. arenas, bump allocators), Memcheck sees only
a single large block. Overflows within that block or reading from uninitialized
regions only triggers a generic "uninitialized value" errors.

By explicitly defining accessible regions ([`MemState::Undefined`]) and
restricting access to free space ([`MemState::NoAccess`]), we can turn generic
errors into precise reports.

```rust, no_run
use crabgrind::memcheck as mc;

struct BumpAllocator {
    start: *mut u8,
    offset: usize,
    capacity: usize,
}

impl BumpAllocator {
    fn new(capacity: usize) -> Self {
        let ptr = unsafe {
            libc::mmap(
                std::ptr::null_mut(),
                capacity,
                libc::PROT_READ | libc::PROT_WRITE,
                libc::MAP_ANONYMOUS | libc::MAP_PRIVATE,
                -1,
                0,
            )
        };

        // Mark entire reserved region as inaccessible.
        // This catches stray reads/writes into the "free" part of the arena.
        mc::mark_memory(ptr as _, capacity, mc::MemState::NoAccess);

        // Name the block for better error messages.
        let block_description = std::ffi::CString::new("BumpAllocator").unwrap();
        mc::create_block(ptr as _, capacity, block_description);

        BumpAllocator {
            start: ptr as *mut u8,
            offset: 0,
            capacity,
        }
    }

    fn alloc(&mut self, size: usize) -> *mut u8 {
        let ptr = unsafe { self.start.add(self.offset) };

        // Mark newly allocated slice as "undefined".
        // This allows the program to write to it, but flags uninitialized reads.
        mc::mark_memory(ptr as _, size, mc::MemState::Undefined);

        self.offset += size;
        ptr
    }
}

fn main() {
    let mut bump = BumpAllocator::new(4096);

    let ptr = bump.alloc(100);
    unsafe { ptr.write(42) }; // Valid write.

    // Invalid access:
    // Attempting to write into the gap or the unallocated heap
    // will cause a precise error, rather than a silent corruption.
    unsafe {
        ptr.add(111).write(0);
    }
}
```

> Run with Memcheck
>
> ```text
> :~$ valgrind --tool=memcheck target/debug/mark_memory
> ```

# Errors

- [`UnaddressableBytes`]UnaddressableBytes - containing the number of
  unaddressable bytes if `mark` is set to
  [`MemState::DefinedIfAddressable`]MemState::DefinedIfAddressable and not all
  bytes in the range were addressable

## Note

Requires Valgrind **3.6** for
[`MemState::DefinedIfAddressable`](MemState::DefinedIfAddressable). All other
variants are supported in Valgrind **3.0** or higher.