passable_guard

Crates.iopassable_guard
lib.rspassable_guard
version1.0.0
sourcesrc
created_at2021-07-28 15:05:53.028943
updated_at2021-07-28 15:05:53.028943
descriptionA Guard to help you catch leakage of pointers that you pass over a FFI boundary
homepagehttps://github.com/estchd/passable_guard
repositoryhttps://github.com/estchd/passable_guard
max_upload_size
id428390
size26,738
Erik Schulze (estchd)

documentation

https://docs.rs/passable_guard

README

Passable Guard

The Passable Guard Crate provides a way to check for FFI memory leaks at runtime.

This is achieved by providing a PassableContainer class that encapsulates a Passable Object that can be converted to a raw pointer to pass it over a FFI boundary.

This PassableContainer combines the raw pointer with a PassableGuard when converting the Passable.

This PassableGuard will panic if it is dropped before recombining it with the raw pointer.

That way, you will at least get a panic instead of leaking memory

Example

For this example, we will create a CString and pass it to a fictional FFI function setName, using a PassableContainer to guard against Memory Leaks

use std::ffi::CString;
use passable_guard::PassableContainer;

extern "C" {
    /// Takes a pointer to a NULL-Terminated utf-8 string
    /// Returns 0 on failure and >0 on success
    fn setName(ptr: *mut u8) -> u8;
}

fn passable_example(name: CString) -> Result<(),()> {
    let passable = PassableContainer::new(name); // Create the Container from the name CString

    let (guard, ptr) = passable.pass(); // Convert the Container into a raw pointer (and get the guard for it as well)

    let result = unsafe {
         setName(ptr) // Call the FFI function and give it our pointer
    };

    unsafe {
        // Reconstitute the Guard and Pointer back into a Container
        // The pointers will be the same since we use the pointer we got from the pass method
        // This might cause UB if setName modifies the Memory
        guard.reconstitute(ptr).unwrap();
    }
    drop(ptr); // Drop the Pointer so we do not use it again

    return if result == 0 {
        Err(())
    }
    else {
        Ok(())
    }
}

Let's look at an example that Panics

use std::ffi::CString;
use passable_guard::PassableContainer;

extern "C" {
    /// Takes a pointer to a NULL-Terminated utf-8 string
    /// Returns 0 on failure and >0 on success
    fn setName(ptr: *mut u8) -> u8;
}

fn passable_example(name: CString) -> Result<(),()> {
    let passable = PassableContainer::new(name); // Create the Container from the name CString

    let (guard, ptr) = passable.pass(); // Convert the Container into a raw pointer (and get the guard for it as well)

    let result = unsafe {
         setName(ptr) // Call the FFI function and give it our pointer
    };

    // Drop the Pointer so we do not use it again
    // This means that we cannot possibly reconstitute the Guard and pointer
    drop(ptr);

    return if result == 0 {
        Err(())
    }
    else {
        Ok(())
    }
    // The Function will panic here since the Guard has been dropped without being reconstituted
    // Without the Guard, we would have now subtly leaked the String Memory
}
Commit count: 9

cargo fmt