Skip to content

WeightedIndex panics instead of returning InvalidWeight for infinite floating-point weights #1818

Description

@kaixinlalala

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    X-bugType: bug report

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions