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

int_in_range can return out-of-range values

jnb opened this issue · comments

I was able to get 0 out of int_in_range(1..=i32::MAX), which is less than the minimum specified value of 1!

Here's what I think is happening in int_in_range_impl:

  • Suppose when we mod our result we get i32::MAX - 1
  • We then do a wrapping_add of start (1) to give i32::MAX
  • T::from_widest then internally mods by i32::MAX to give 0

I'm not too sure of the general fix here, but I worked around this issue by instead using int_in_range(0..=i32::MAX - 1) + 1.

Thanks for filing an issue and including details. Definitely something we should fix!

Would be nice to get a concrete input such that calling u.int_in_range(...) returns an out-of-range int, if anyone has time to make a reproducer.

EDIT: Ah, sorry, I just realized that this is probably useless out of context, because I have other lines that will be consuming data from the same source. Leaving this here in the hope of being able to update with an actual minimal repro...

I just ran this:

// ...
            outer_range: u.int_in_range(0u8..=2)?..u.int_in_range(253..=255)?,
// ...

And ended up with this:

// ...
	    outer_range: 0..0,
// ...

I was glancing through open issues and saw this and thought it ought to be easy to write a unit test for. And it is! Here's one that fails on master:

    #[test]
    fn int_in_range_overflow() {
        let mut u = Unstructured::new(&[254]);
        let x = u.int_in_range(1..=u8::MAX).unwrap();
        assert!(x != 0);
    }

I found that by writing a loop to exhaustively try every 1-byte input to Unstructured::new. I've done zero root-cause analysis so I have no idea why this fails, but I hope this helps.