File bind-9.16.6-CVE-2025-40780.patch of Package bind.41334

commit 2153bfa40e642a3407a4169270a5099821ca25d9
Author: Michał Kępień <michal@isc.org>
Date:   Wed Oct 22 18:45:05 2025 +0200

    [9.16] [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.16' into 'bind-9.16-release'
    
    See merge request isc-private/bind9!848

diff --git a/lib/isc/include/isc/os.h b/lib/isc/include/isc/os.h
index b541612f00..dc809f319c 100644
--- a/lib/isc/include/isc/os.h
+++ b/lib/isc/include/isc/os.h
@@ -18,6 +18,11 @@
 
 ISC_LANG_BEGINDECLS
 
+/*%<
+ * Hardcode the L1 cacheline size of the CPU to 64.
+ */
+#define ISC_OS_CACHELINE_SIZE 64
+
 unsigned int
 isc_os_ncpus(void);
 /*%<
diff --git a/lib/isc/include/isc/random.h b/lib/isc/include/isc/random.h
index cdeaa3e44b..209329be41 100644
--- a/lib/isc/include/isc/random.h
+++ b/lib/isc/include/isc/random.h
@@ -18,7 +18,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 02b92260db..73ea3d7000 100644
--- a/lib/isc/random.c
+++ b/lib/isc/random.c
@@ -29,131 +29,136 @@
  */
 
 #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/platform.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.
- */
-#include "xoshiro128starstar.c"
+#define ISC_RANDOM_BUFSIZE (ISC_OS_CACHELINE_SIZE / sizeof(uint32_t))
 
-ISC_THREAD_LOCAL isc_once_t isc_random_once = ISC_ONCE_INIT;
+ISC_THREAD_LOCAL uint32_t isc__random_pool[ISC_RANDOM_BUFSIZE];
+ISC_THREAD_LOCAL size_t isc__random_pos = ISC_RANDOM_BUFSIZE;
 
-static void
-isc_random_initialize(void) {
-	int useed[4] = { 0, 0, 0, 1 };
+static uint32_t
+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(buflen == 0 || buf != NULL);
 
-	REQUIRE(buf != NULL);
-	REQUIRE(buflen > 0);
-
-	RUNTIME_CHECK(isc_once_do(&isc_random_once, isc_random_initialize) ==
-		      ISC_R_SUCCESS);
-
-	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/lib/isc/tests/random_test.c b/lib/isc/tests/random_test.c
index 2afbbb2308..03827ea44d 100644
--- a/lib/isc/tests/random_test.c
+++ b/lib/isc/tests/random_test.c
@@ -345,7 +345,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;
openSUSE Build Service is sponsored by