Description:
rand::distr::weighted::WeightedIndex can panic when given infinite floating-point weights.
This affects both:
WeightedIndex::new
WeightedIndex::update_weights
Both APIs are safe Rust APIs and return Result, so invalid weights should be reported through the error type instead of causing an internal panic.
The documented Error::InvalidWeight variant says it covers weights that are negative, too large for the distribution, or not a valid number. Therefore, f64::INFINITY seems like it should be rejected as Err(Error::InvalidWeight) rather than reaching an internal .unwrap().
Environment:
rand version: 0.10.2
rustc version: 1.94.1
OS: Ubuntu 20.04.6 LTS
PoC 1: WeightedIndex::new
extern crate rand;
fn main() {
let _ = rand::distr::weighted::WeightedIndex::new([f64::INFINITY]);
}
PoC 2: WeightedIndex::update_weights
extern crate rand;
fn main() {
let mut dist = rand::distr::weighted::WeightedIndex::new([1.0f64, 2.0]).unwrap();
let _ = dist.update_weights(&[(0, &f64::INFINITY)]);
}
Expected behavior:
The function should not panic. Since WeightedIndex::new returns a Result, it should return an error, likely:
Err(rand::distr::weighted::Error::InvalidWeight)
This seems consistent with the error documentation, where InvalidWeight is described as covering weights that are negative, too large for the distribution, or not a valid number.
Actual behavior
WeightedIndex::new
thread 'main' panicked at /Rust-Lib-Testing/test/tests/crates/rand-0.10.2/src/distr/weighted/weighted_index.rs:130:65:
called `Result::unwrap()` on an `Err` value: NonFinite
WeightedIndex::update_weights
thread 'main' panicked at /Rust-Lib-Testing/test/tests/crates/rand-0.10.2/src/distr/weighted/weighted_index.rs:236:85:
called `Result::unwrap()` on an `Err` value: NonFinite
Root cause:
In src/distr/weighted/weighted_index.rs, WeightedIndex::new accepts non-negative floating-point weights and accumulates them into total_weight.
For f64::INFINITY, the existing checks do not reject the value:
|
if !(total_weight >= zero) { |
|
return Err(Error::InvalidWeight); |
|
} |
Since INFINITY >= 0.0 is true, the value is accepted.
Later, the code constructs a uniform sampler:
let distr = X::Sampler::new(zero, total_weight.clone()).unwrap();
and in update_weights:
self.weight_distribution = X::Sampler::new(zero, self.total_weight.clone()).unwrap();
For floating-point weights, X::Sampler::new(0.0, f64::INFINITY) returns Err(NonFinite). Because the result is unwrapped internally, this becomes a panic instead of a recoverable WeightedIndex error.
Description:
rand::distr::weighted::WeightedIndexcan panic when given infinite floating-point weights.This affects both:
WeightedIndex::newWeightedIndex::update_weightsBoth APIs are safe Rust APIs and return
Result, so invalid weights should be reported through the error type instead of causing an internal panic.The documented
Error::InvalidWeightvariant says it covers weights that are negative, too large for the distribution, or not a valid number. Therefore,f64::INFINITYseems like it should be rejected asErr(Error::InvalidWeight)rather than reaching an internal.unwrap().Environment:
rand version: 0.10.2
rustc version: 1.94.1
OS: Ubuntu 20.04.6 LTS
PoC 1:
WeightedIndex::newPoC 2:
WeightedIndex::update_weightsExpected behavior:
The function should not panic. Since
WeightedIndex::newreturns aResult, it should return an error, likely:This seems consistent with the error documentation, where
InvalidWeightis described as covering weights that are negative, too large for the distribution, or not a valid number.Actual behavior
WeightedIndex::newWeightedIndex::update_weightsRoot cause:
In
src/distr/weighted/weighted_index.rs,WeightedIndex::newaccepts non-negative floating-point weights and accumulates them intototal_weight.For
f64::INFINITY, the existing checks do not reject the value:rand/src/distr/weighted/weighted_index.rs
Lines 109 to 111 in 486da44
Since
INFINITY >= 0.0is true, the value is accepted.Later, the code constructs a uniform sampler:
and in
update_weights:For floating-point weights,
X::Sampler::new(0.0, f64::INFINITY)returnsErr(NonFinite). Because the result is unwrapped internally, this becomes a panic instead of a recoverableWeightedIndexerror.