Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SIGSEGV on certain values #30

Closed
ysimonson opened this issue Jan 22, 2020 · 12 comments · Fixed by #31
Closed

SIGSEGV on certain values #30

ysimonson opened this issue Jan 22, 2020 · 12 comments · Fixed by #31

Comments

@ysimonson
Copy link

I think this is an issue with arbitrary, but let me know where I should move it if not.

I'm observing an issue where derived arbitrary types trigger a SIGSEGV when running cargo-fuzz. Here's a minimal example:

#![no_main]

use libfuzzer_sys::fuzz_target;
use arbitrary::Arbitrary;

#[derive(Arbitrary, Clone, Debug, PartialEq)]
pub enum Op {
    A(A),
    B(B),
}

#[derive(Arbitrary, Clone, Debug, PartialEq)]
pub enum A {
    Leaf,
    B(Box<B>),
}

#[derive(Arbitrary, Clone, Debug, PartialEq)]
pub enum B {
    Leaf,
    A(Box<A>),
}

fuzz_target!(|ops: Vec<Op>| {
});

Running this on the latest nightly (both mac and linux) triggers a SIGSEGV:

INFO: Seed: 3703294504
INFO: Loaded 1 modules   (17415 inline 8-bit counters): 17415 [0x10f5ef708, 0x10f5f3b0f), 
INFO: Loaded 1 PC tables (17415 PCs): 17415 [0x10f5f3b10,0x10f637b80), 
INFO:       59 files found in /Users/yusuf/Desktop/sandbox/fuzz-bug/fuzz/corpus/fuzz_target_1
INFO: -max_len is not provided; libFuzzer will not generate inputs larger than 4096 bytes
────────────────────────────────────────────────────────────────────────────────

Error: Fuzz target exited with signal: 11

Any of these changes will remove the segfault:

  1. Changing the fuzz target's input from Vec<Op> to just Op.
  2. Removing the B enum variant from A
  3. Removing the A enum variant from B
@ysimonson
Copy link
Author

It looks like implementing Arbitrary by hand rather than deriving it also fixes the issue.

@Manishearth
Copy link
Member

Yeah it's a stack overflow. You can repro it by hand by just trying to construct Vec<Op> from Arbitrary

@Manishearth
Copy link
Member

oh, size_hint doesn't work for recursive structures.

cc @fitzgen thoughts on how to fix this?

@Manishearth
Copy link
Member

A potential route is to turn size_hint into a const fn (it's intended to const fold away anyway), and document that for recursive datastructures you should mark the struct with #[arbitrary::recursive] or something. The const fns will make it possible to catch this at compile time.

@fitzgen
Copy link
Member

fitzgen commented Jan 22, 2020

Another option would be to pass in (or have a thread local, ew) a HashSet<TypeId> and if this type's id is already in the set, then return an unknown upper bound, but if not, do what we do now and also add this type's id to the set for the future.

@fitzgen
Copy link
Member

fitzgen commented Jan 22, 2020

I tried making size_hint a const fn at first, but we can't match on or destructure the tuple to do the and and or operations inside a const context. Or at least, I don't know of a way to do that.

@Manishearth
Copy link
Member

Another option would be to pass in (or have a thread local, ew) a HashSet and if this type's id is already in the set, then return an unknown upper bound, but if not, do what we do now and also add this type's id to the set for the future.

The hope is that this function const folds away, so I don't want to introduce global state

@Manishearth
Copy link
Member

Yeah, i'm not sure how to make this work without const_if_match, but IIRC these things should stabilize soonish

@fitzgen
Copy link
Member

fitzgen commented Jan 22, 2020

We could probably preserve the const-folding-away by introducing a depth parameter, and if the depth is too big, say larger than 20, returning None for the upper bound.

@Manishearth
Copy link
Member

maybe, sounds a bit precarious

@Manishearth
Copy link
Member

I just want a type-level garbage collecter is that too much to ask for

@fitzgen
Copy link
Member

fitzgen commented Jan 22, 2020

#[derive(Arbitrary)]
struct Precarious {
    maybe: Option<Box<Precarious>>,
}

fitzgen added a commit to fitzgen/rust_arbitrary that referenced this issue Jan 22, 2020
Note that the implementations of `size_hint` only need to increment the depth at
indirection points where recursion can happen, e.g. at a `Box`. We take
advantage of this in the impls for `std` types, but conservatively do not take
advantage of that in the custom derive, since it doesn't have the necessary
info.

Fixes rust-fuzz#30
fitzgen added a commit to fitzgen/rust_arbitrary that referenced this issue Jan 22, 2020
Note that the implementations of `size_hint` only need to increment the depth at
indirection points where recursion can happen, e.g. at a `Box`. We take
advantage of this in the impls for `std` types, but conservatively do not take
advantage of that in the custom derive, since it doesn't have the necessary
info.

Fixes rust-fuzz#30
fitzgen added a commit to fitzgen/rust_arbitrary that referenced this issue Jan 22, 2020
Note that the implementations of `size_hint` only need to increment the depth at
indirection points where recursion can happen, e.g. at a `Box`. We take
advantage of this in the impls for `std` types, but conservatively do not take
advantage of that in the custom derive, since it doesn't have the necessary
info.

Fixes rust-fuzz#30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants