arbitrary
arbitrary copied to clipboard
int_in_range can return out-of-range values
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
ofstart
(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.