File bind-9.18-CVE-2025-40780.patch of Package bind.41290
commit 007c95c4068990317e37ac8818be675e73cd3534
Author: Michał Kępień <michal@isc.org>
Date: Thu Oct 2 14:26:41 2025 +0200
[9.18] [CVE-2025-40780] sec: usr: Cache-poisoning due to weak pseudo-random number generator
It was discovered during research for an upcoming academic paper that a
xoshiro128\*\* internal state can be recovered by an external 3rd party,
allowing the prediction of UDP ports and DNS IDs in outgoing queries.
This could lead to an attacker spoofing the DNS answers with great
efficiency and poisoning the DNS cache.
The internal random generator has been changed to a cryptographically
secure pseudo-random generator.
ISC would like to thank Prof. Amit Klein and Omer Ben Simhon from Hebrew
University of Jerusalem for bringing this vulnerability to our
attention.
Backport of !831
Closes isc-projects/bind9#5484
Merge branch '5484-security-make-isc_random-csprng-9.18' into 'v9.18.40-release'
See merge request isc-private/bind9!847
diff --git a/lib/isc/include/isc/random.h b/lib/isc/include/isc/random.h
index 1e30d0c87d..fd55343778 100644
--- a/lib/isc/include/isc/random.h
+++ b/lib/isc/include/isc/random.h
@@ -20,7 +20,7 @@
#include <isc/types.h>
/*! \file isc/random.h
- * \brief Implements wrapper around a non-cryptographically secure
+ * \brief Implements wrapper around a cryptographically secure
* pseudo-random number generator.
*
*/
diff --git a/lib/isc/random.c b/lib/isc/random.c
index fb0466953d..6f37f5dedf 100644
--- a/lib/isc/random.c
+++ b/lib/isc/random.c
@@ -31,176 +31,135 @@
*/
#include <inttypes.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
+#include <stdio.h>
-#include <isc/once.h>
+#include <isc/os.h>
#include <isc/random.h>
-#include <isc/result.h>
#include <isc/thread.h>
-#include <isc/types.h>
#include <isc/util.h>
#include "entropy_private.h"
-/*
- * The specific implementation for PRNG is included as a C file
- * that has to provide a static variable named seed, and a function
- * uint32_t next(void) that provides next random number.
- *
- * The implementation must be thread-safe.
- */
-
-/*
- * Two contestants have been considered: the xoroshiro family of the
- * functions by Villa&Blackman, and PCG by O'Neill. After
- * consideration, the xoshiro128starstar function has been chosen as
- * the uint32_t random number provider because it is very fast and has
- * good enough properties for our usage pattern.
- */
-
-/*
- * Written in 2018 by David Blackman and Sebastiano Vigna (vigna@acm.org)
- *
- * To the extent possible under law, the author has dedicated all
- * copyright and related and neighboring rights to this software to the
- * public domain worldwide. This software is distributed without any
- * warranty.
- *
- * See <http://creativecommons.org/publicdomain/zero/1.0/>.
- */
+#define ISC_RANDOM_BUFSIZE (ISC_OS_CACHELINE_SIZE / sizeof(uint32_t))
-/*
- * This is xoshiro128** 1.0, our 32-bit all-purpose, rock-solid generator.
- * It has excellent (sub-ns) speed, a state size (128 bits) that is large
- * enough for mild parallelism, and it passes all tests we are aware of.
- *
- * For generating just single-precision (i.e., 32-bit) floating-point
- * numbers, xoshiro128+ is even faster.
- *
- * The state must be seeded so that it is not everywhere zero.
- */
-static thread_local uint32_t seed[4] = { 0 };
+thread_local static uint32_t isc__random_pool[ISC_RANDOM_BUFSIZE];
+thread_local static size_t isc__random_pos = ISC_RANDOM_BUFSIZE;
static uint32_t
-rotl(const uint32_t x, int k) {
- return (x << k) | (x >> (32 - k));
-}
-
-static uint32_t
-next(void) {
- uint32_t result_starstar, t;
-
- result_starstar = rotl(seed[0] * 5, 7) * 9;
- t = seed[1] << 9;
-
- seed[2] ^= seed[0];
- seed[3] ^= seed[1];
- seed[1] ^= seed[2];
- seed[0] ^= seed[3];
-
- seed[2] ^= t;
-
- seed[3] = rotl(seed[3], 11);
-
- return result_starstar;
-}
-
-static thread_local isc_once_t isc_random_once = ISC_ONCE_INIT;
-
-static void
-isc_random_initialize(void) {
- int useed[4] = { 0, 0, 0, 1 };
+random_u32(void) {
#if FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
/*
- * Set a constant seed to help in problem reproduction should fuzzing
- * find a crash or a hang. The seed array must be non-zero else
- * xoshiro128starstar will generate an infinite series of zeroes.
+ * A fixed stream of numbers helps with problem reproduction when
+ * fuzzing. The first result needs to be non-zero as expected by
+ * random_test.c (it starts with ISC_RANDOM_BUFSIZE, see above).
*/
-#else /* if FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION */
- isc_entropy_get(useed, sizeof(useed));
+ return (uint32_t)(isc__random_pos++);
#endif /* if FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION */
- memmove(seed, useed, sizeof(seed));
+
+ if (isc__random_pos == ISC_RANDOM_BUFSIZE) {
+ isc_entropy_get(isc__random_pool, sizeof(isc__random_pool));
+ isc__random_pos = 0;
+ }
+
+ return isc__random_pool[isc__random_pos++];
}
uint8_t
isc_random8(void) {
- RUNTIME_CHECK(isc_once_do(&isc_random_once, isc_random_initialize) ==
- ISC_R_SUCCESS);
- return next() & 0xff;
+ return (uint8_t)random_u32();
}
uint16_t
isc_random16(void) {
- RUNTIME_CHECK(isc_once_do(&isc_random_once, isc_random_initialize) ==
- ISC_R_SUCCESS);
- return next() & 0xffff;
+ return (uint16_t)random_u32();
}
uint32_t
isc_random32(void) {
- RUNTIME_CHECK(isc_once_do(&isc_random_once, isc_random_initialize) ==
- ISC_R_SUCCESS);
- return next();
+ return random_u32();
}
void
isc_random_buf(void *buf, size_t buflen) {
- int i;
- uint32_t r;
-
- REQUIRE(buf != NULL);
- REQUIRE(buflen > 0);
-
- RUNTIME_CHECK(isc_once_do(&isc_random_once, isc_random_initialize) ==
- ISC_R_SUCCESS);
+ REQUIRE(buflen == 0 || buf != NULL);
- for (i = 0; i + sizeof(r) <= buflen; i += sizeof(r)) {
- r = next();
- memmove((uint8_t *)buf + i, &r, sizeof(r));
+ if (buf == NULL || buflen == 0) {
+ return;
}
- r = next();
- memmove((uint8_t *)buf + i, &r, buflen % sizeof(r));
- return;
+
+ isc_entropy_get(buf, buflen);
}
uint32_t
-isc_random_uniform(uint32_t upper_bound) {
- /* Copy of arc4random_uniform from OpenBSD */
- uint32_t r, min;
-
- RUNTIME_CHECK(isc_once_do(&isc_random_once, isc_random_initialize) ==
- ISC_R_SUCCESS);
-
- if (upper_bound < 2) {
- return 0;
- }
-
-#if (ULONG_MAX > 0xffffffffUL)
- min = 0x100000000UL % upper_bound;
-#else /* if (ULONG_MAX > 0xffffffffUL) */
- /* Calculate (2**32 % upper_bound) avoiding 64-bit math */
- if (upper_bound > 0x80000000) {
- min = 1 + ~upper_bound; /* 2**32 - upper_bound */
- } else {
- /* (2**32 - (x * 2)) % x == 2**32 % x when x <= 2**31 */
- min = ((0xffffffff - (upper_bound * 2)) + 1) % upper_bound;
- }
-#endif /* if (ULONG_MAX > 0xffffffffUL) */
-
+isc_random_uniform(uint32_t limit) {
/*
- * This could theoretically loop forever but each retry has
- * p > 0.5 (worst case, usually far better) of selecting a
- * number inside the range we need, so it should rarely need
- * to re-roll.
+ * Daniel Lemire's nearly-divisionless unbiased bounded random numbers.
+ *
+ * https://lemire.me/blog/?p=17551
+ *
+ * The raw random number generator `next()` returns a 32-bit value.
+ * We do a 64-bit multiply `next() * limit` and treat the product as a
+ * 32.32 fixed-point value less than the limit. Our result will be the
+ * integer part (upper 32 bits), and we will use the fraction part
+ * (lower 32 bits) to determine whether or not we need to resample.
*/
- for (;;) {
- r = next();
- if (r >= min) {
- break;
+ uint64_t num = (uint64_t)random_u32() * (uint64_t)limit;
+ /*
+ * In the fast path, we avoid doing a division in most cases by
+ * comparing the fraction part of `num` with the limit, which is
+ * a slight over-estimate for the exact resample threshold.
+ */
+ if ((uint32_t)(num) < limit) {
+ /*
+ * We are in the slow path where we re-do the approximate test
+ * more accurately. The exact threshold for the resample loop
+ * is the remainder after dividing the raw RNG limit `1 << 32`
+ * by the caller's limit. We use a trick to calculate it
+ * within 32 bits:
+ *
+ * (1 << 32) % limit
+ * == ((1 << 32) - limit) % limit
+ * == (uint32_t)(-limit) % limit
+ *
+ * This division is safe: we know that `limit` is strictly
+ * greater than zero because of the slow-path test above.
+ */
+ uint32_t residue = (uint32_t)(-limit) % limit;
+ /*
+ * Unless we get one of `N = (1 << 32) - residue` valid
+ * values, we reject the sample. This `N` is a multiple of
+ * `limit`, so our results will be unbiased; and `N` is the
+ * largest multiple that fits in 32 bits, so rejections are as
+ * rare as possible.
+ *
+ * There are `limit` possible values for the integer part of
+ * our fixed-point number. Each one corresponds to `N/limit`
+ * or `N/limit + 1` possible fraction parts. For our result to
+ * be unbiased, every possible integer part must have the same
+ * number of possible valid fraction parts. So, when we get
+ * the superfluous value in the `N/limit + 1` cases, we need
+ * to reject and resample.
+ *
+ * Because of the multiplication, the possible values in the
+ * fraction part are equally spaced by `limit`, with varying
+ * gaps at each end of the fraction's 32-bit range. We will
+ * choose a range of size `N` (a multiple of `limit`) into
+ * which valid fraction values must fall, with the rest of the
+ * 32-bit range covered by the `residue`. Lemire's paper says
+ * that exactly `N/limit` possible values spaced apart by
+ * `limit` will fit into our size `N` valid range, regardless
+ * of the size of the end gaps, the phase alignment of the
+ * values, or the position of the range.
+ *
+ * So, when a fraction value falls in the `residue` outside
+ * our valid range, it is superfluous, and we resample.
+ */
+ while ((uint32_t)(num) < residue) {
+ num = (uint64_t)random_u32() * (uint64_t)limit;
}
}
-
- return r % upper_bound;
+ /*
+ * Return the integer part (upper 32 bits).
+ */
+ return (uint32_t)(num >> 32);
}
diff --git a/tests/isc/random_test.c b/tests/isc/random_test.c
index ccba317023..52f219bb3a 100644
--- a/tests/isc/random_test.c
+++ b/tests/isc/random_test.c
@@ -321,7 +321,9 @@ random_test(pvalue_func_t *func, isc_random_func test_func) {
}
break;
case ISC_RANDOM_BYTES:
- isc_random_buf(values, sizeof(values));
+ for (i = 0; i < ARRAY_SIZE(values); i++) {
+ values[i] = isc_random32();
+ }
break;
case ISC_RANDOM_UNIFORM:
uniform_values = (uint16_t *)values;