rust-fuzz / arbitrary

Generating structured data from arbitrary, unstructured input.

Home Page:https://docs.rs/arbitrary/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Support for `allocator_api` feature and `allocator-api2` crate

cloneable opened this issue · comments

Hi, would you be interested in adding support for the allocator_api feature (for nightly) and the allocator-api2 crate (for stable)? I'm using a-a2 for my AST types and wanted to fuzzy test a code generator, but there doesn't seem to be a way to inject an allocator into Arbitrary::arbitrary().

I'm thinking Unstructured could provide an allocator() method that returns the allocator passed to Unstructured::with_allocator(data, my_allocator). Problem is that this requires Unstructured to be generic. I don't know if it's enough to set Global as default value to not break the API.

Instead of supporting a custom allocator directly Unstructured could expose some generic extra state that could contain the allocator. That's more flexible.

I made some progress on other parts of my project and realized that the (current) allocator API is too "invasive" may not be worth the hassle, and because String isn't even covered yet I will continue without using an arena allocator.

To test an allocator with arbitrary, I recommend doing something like this:

struct Size(usize);

impl<'a> Arbitrary<'a> for Size {
    // clamp as necessary...
}

struct Align(usize);

impl<'a> Arbitrary<'a> for Align {
    // round to pow 2 and clamp as necessary...
}

#[derive(Arbitrary)]
enum Op {
    Alloc { size: Size, align: Align },
    Dealloc { allocation: usize },
    Realloc { allocation: usize, size: Size, align: Align },
    // ...
}

fn test_allocator(allocator: &impl Allocator, ops: &[Op]) {
    let mut allocs = vec![];

    for op in ops {
        match op {
            Op::Alloc { size, align } => {
                let layout = Layout::new(size, align);
                let ptr = allocator.allocate(layout);
                allocs.push((layout, ptr));
            }
            Op::Dealloc { allocation } => {
                if allocs.is_empty() {
                    continue;
                }
                let i = allocation % allocs.len();
                let (layout, ptr) = allocs.swap_remove(i).unwrap();
                allocator.deallocate(ptr, layout);
            }
            // ...
        }
    }

    // Free any remaining allocations in allocs...
}

libfuzzer_sys::fuzz_target!(|ops: Vec<Op>| {
    let allocator = MyAllocator::new();
    test_allocator(&allocator, &ops);
});

Right, thanks! Sorry, I should have explained better. I wanted to implement Arbitrary for types that have <A: Allocator>, like Vec and Box, because my types have such containers. I guess I could have kept it simple for tests and only impl Arbitrary for MyType<Global>. In the end I decided to rip out the allocator stuff because too many other types and functions would need to be made generic and because String<A>, probably the most important type, isn't going to be possible for another year.

That's all. Still, adding some generic extra state to Unstructured could be useful. Maybe something for v2.