How to fix lifetime issues to prevent use-after-free error in miri

A wrapper is created around bumpalo allocator to side step the issue of annotating everything that has to be allocated on the allocator with a lifetime. A guard was created with the idea to clean up the allocator when the guard is dropped.

thread_local! {
    static CURRENT_ALLOCATOR: Cell<Option<&'static WrapAllocator>> = const { Cell::new(None) };
}

pub struct WrapAllocator {
    bump: Bump,
}

#[derive(Clone, Copy)]
pub struct LimboAllocator {
    allocator: Option<&'static WrapAllocator>,
}

pub struct AllocatorGuard {}

impl Drop for AllocatorGuard {
    fn drop(&mut self) {
        println!("dropping!!!");
        CURRENT_ALLOCATOR.set(None);
    }
}

impl WrapAllocator {
    pub fn new() -> Self {
        println!("new allocator!!!");
        Self { bump: Bump::new() }
    }

    pub unsafe fn guard(&self) -> AllocatorGuard {
        let static_ref = unsafe { transmute::<&WrapAllocator, &'static WrapAllocator>(self) };
        CURRENT_ALLOCATOR.set(Some(static_ref));
        AllocatorGuard {}
    }
}

And when I tried to use this with a parser miri throws a UB error that has use after free.

This minimal example that reproduces the parser and allocator interaction:

    struct TestParser<'a> {
        some_reference: &'a str,
        vector: Vec<String>,
        _allocator: WrapAllocator,
        _guard: AllocatorGuard,
    }

    impl<'a> TestParser<'a> {
        fn new(reference: &'a str) -> Self {
            let allocator = WrapAllocator::new();
            
            let guard = unsafe { allocator.guard() };
            
            let mut vector = Vec::new();
            
            vector.push(String::from("We are"));
            vector.push(String::from("So Back"));
            
            TestParser {
                some_reference: reference,
                vector,
                _allocator: allocator,
                _guard: guard,
            }
        }
        
        fn add_item(&mut self, item: String) {
            self.vector.push(item);
        }
    }

    #[test]
    fn test_allocator_with_different_lifetimes() {
        {
            let text = String::from("test string");       
            {
                let mut parser = TestParser::new(&text);
                parser.add_item(String::from("Additional item"));
            }
        }
    }

Rust playground with complete code: Rust Playground

How do I solve this use-after-free error? Can someone please help me?

in this function you create a WrapAllocator and then create references to it. Then you move it when you return from the function. The references still point at the old memory. You could allocate it on the heap to ensure that it doesn't move.
You have the issue, that the AllocatorGuard in no way references the WrapAllocator the ThreadLocal is pointing to. I don't think you can create a sound interface this way. Could you maybe store the WrapAllocator in the Threadlocal instead of a reference to it?
If you continue this design you should probably use raw pointers instead of references. references have no advantage if you transmute to static anyways and it's pretty easy to get UB with them.

1 Like