From 4e8c7a4ca2963797d0ec414d04b6239ece905165 Mon Sep 17 00:00:00 2001 From: Gautier Minster Date: Tue, 12 Jan 2021 17:30:22 +0200 Subject: [PATCH 1/2] distributions/uniform: fix panic in gen_range(0..=MAX) This commit fixes a panic when generating a single sample in an inclusive range that spans the entire integer range, eg for u8: ```rust rng.gen_range(0..=u8::MAX) // panicked at 'attempt to add with overflow', src/distributions/uniform.rs:529:42 ``` [Playground example](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&code=use%20rand%3A%3ARng%3B%0A%0Afn%20main()%20%7B%0A%20%20%20%20rand%3A%3Athread_rng().gen_range(0u8..%3D255u8)%3B%0A%7D). The cause is a discrepancy between the "single sample" and the "many samples" codepaths: ```rust // Ok UniformSampler::new_inclusive(u8::MIN, u8::MAX).sample(&mut rng); // Panic UniformSampler::sample_single_inclusive(u8::MIN, u8::MAX, &mut rng); ``` In `sample`, a `range` of 0 is interpreted to mean "sample from the whole range". In `sample_range_inclusive`, no check is performed, which leads to overflow when computing the `ints_to_reject`. **Testing** - Added a test case. - Old code panics, new code passes. --- src/distributions/uniform.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/distributions/uniform.rs b/src/distributions/uniform.rs index bbd96948f8c..e4a6407fd66 100644 --- a/src/distributions/uniform.rs +++ b/src/distributions/uniform.rs @@ -521,6 +521,12 @@ macro_rules! uniform_int_impl { let high = *high_b.borrow(); assert!(low <= high, "UniformSampler::sample_single_inclusive: low > high"); let range = high.wrapping_sub(low).wrapping_add(1) as $unsigned as $u_large; + // If the above resulted in wrap-around to 0, the range is $ty::MIN..=$ty::MAX, + // and any integer will do. + if range == 0 { + return rng.gen(); + } + let zone = if ::core::$unsigned::MAX <= ::core::u16::MAX as $unsigned { // Using a modulus is faster than the approximation for // i8 and i16. I suppose we trade the cost of one @@ -1235,6 +1241,11 @@ mod tests { let v = <$ty as SampleUniform>::Sampler::sample_single(low, high, &mut rng); assert!($le(low, v) && $lt(v, high)); } + + for _ in 0..1000 { + let v = <$ty as SampleUniform>::Sampler::sample_single_inclusive(low, high, &mut rng); + assert!($le(low, v) && $le(v, high)); + } } }}; From 2c9085a2de8864ee327af0311f6a8e5747cf25b7 Mon Sep 17 00:00:00 2001 From: Gautier Minster Date: Tue, 12 Jan 2021 19:49:03 +0200 Subject: [PATCH 2/2] Bump to 0.8.2 and update changelog --- CHANGELOG.md | 5 +++++ Cargo.toml | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b53f95e2064..c0495305fd7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,11 @@ A [separate changelog is kept for rand_core](rand_core/CHANGELOG.md). You may also find the [Upgrade Guide](https://rust-random.github.io/book/update.html) useful. +## [0.8.2] - 2021-01-12 +### Fixes +- Fix panic in `UniformInt::sample_single_inclusive` and `Rng::gen_range` when + providing a full integer range (eg `0..=MAX`) (#1087) + ## [0.8.1] - 2020-12-31 ### Other - Enable all stable features in the playground (#1081) diff --git a/Cargo.toml b/Cargo.toml index 512ed589ac9..69a68444c13 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rand" -version = "0.8.1" +version = "0.8.2" authors = ["The Rand Project Developers", "The Rust Project Developers"] license = "MIT OR Apache-2.0" readme = "README.md"