diff --git a/absl/random/log_uniform_int_distribution.h b/absl/random/log_uniform_int_distribution.h index cbd5e0ca2ba..aac249274bf 100644 --- a/absl/random/log_uniform_int_distribution.h +++ b/absl/random/log_uniform_int_distribution.h @@ -80,7 +80,18 @@ class log_uniform_int_distribution { // which can eliminate some values depending on where the bounds fall. const double inv_log_base = 1.0 / std::log(static_cast(base_)); const double log_range = std::log(static_cast(range()) + 0.5); - log_range_ = static_cast(std::ceil(inv_log_base * log_range)); + const double result = std::ceil(inv_log_base * log_range); + // A base_ of 0 or 1, or a negative base_, violates the base_ > 1 + // precondition and leaves inv_log_base non-finite, so `result` can be + // inf or NaN. Casting such a value to int is undefined behavior; guard + // it so an out-of-contract base yields a defined (if meaningless) + // log_range_ instead. For a valid base_ (> 1), result is a small + // non-negative integer and this guard is a no-op. + log_range_ = + (result >= 0 && + result < static_cast((std::numeric_limits::max)())) + ? static_cast(result) + : 0; } } diff --git a/absl/random/log_uniform_int_distribution_test.cc b/absl/random/log_uniform_int_distribution_test.cc index 591b5b37e48..9190f0f19b4 100644 --- a/absl/random/log_uniform_int_distribution_test.cc +++ b/absl/random/log_uniform_int_distribution_test.cc @@ -116,6 +116,33 @@ TYPED_TEST(LogUniformIntDistributionTypeTest, SerializeTest) { } } +// A base of 1 (or, for signed types, a negative base) violates the base > 1 +// precondition. In debug builds the constructor asserts; in opt builds it must +// still yield a defined object rather than invoking undefined behavior while +// computing log_range_, which casts 1/log(base) * log(range) to int -- a cast +// of a non-finite double (inf/NaN) to int is UB. The bad base can also reach +// the object through operator>> reading an untrusted stream. +TYPED_TEST(LogUniformIntDistributionTypeTest, InvalidBaseIsDefinedInOptMode) { +#if defined(NDEBUG) + absl::InsecureBitGen gen; + + // Direct construction with an out-of-contract base must not invoke UB. + absl::log_uniform_int_distribution dist(0, 100, 1); + auto sample = dist(gen); + EXPECT_GE(sample, dist.min()); + EXPECT_LE(sample, dist.max()); + + // The same bad base arriving through deserialization must also stay defined. + absl::log_uniform_int_distribution after(3, 6, 17); + std::istringstream is("0 100 1"); + is >> after; + EXPECT_EQ(after.base(), static_cast(1)); + sample = after(gen); + EXPECT_GE(sample, after.min()); + EXPECT_LE(sample, after.max()); +#endif // NDEBUG +} + using log_uniform_i32 = absl::log_uniform_int_distribution; class LogUniformIntChiSquaredTest