pseudorand: make the results in deterministic mode per-caller.
What changed, and why it matters
This commit changes how Core Lightning generates 'pseudo-random' numbers when running in deterministic mode (used mainly for reproducible testing). It gives each separate piece of code that asks for a random number its own private counter, so the order in which different callers happen to ask no longer changes the random values they receive. The change is aimed at making tests more stable and easier to reproduce, not at fixing a security vulnerability in live operation.
No immediate security action required. Treat as a normal code-quality/test-reliability change. Reviewers may want to confirm that the per-caller offset does not accidentally weaken deterministic fuzzing coverage or hide real bugs by making test outputs too stable.
Security signals we found
Deterministic randomness path changed, but only when randbytes_overridden() is active
Per-caller offset introduced to reduce order-dependency in tests
No change to live-network ISAAC64 PRNG behavior
No mention of vulnerability, CVE, or security fix in commit message
Evidence from the diff
The patch renames the internal functions to pseudorand_(), pseudorand_u64_(), and pseudorand_double_() and exposes macros that pass a per-call-site static offset pointer. When randbytes_overridden() is true (deterministic/test mode), the code now calls randbytes_() with that caller-specific offset instead of advancing a single shared ISAAC64 stream. In normal operation the existing ISAAC64-based PRNG is still used unchanged. The change is therefore a test-infrastructure improvement to reduce order-dependency of deterministic randomness, not a production cryptographic fix.
Changed components
common/pseudorand.ccommon/pseudorand.hcommon/test/run-psbt_diff.conchaind/test/run-grind_feerate-bug.ctests/fuzz/libfuzz.cwire/test/run-peer-wire.cwire/test/run-tlvstream.cInspect captured patch +40 / −11
diff --git a/common/pseudorand.c b/common/pseudorand.c
index 7f3efafe..52da1e61 100644
--- a/common/pseudorand.c
+++ b/common/pseudorand.c
@@ -26,25 +26,42 @@ static void init_if_needed(void)
}
}
-uint64_t pseudorand(uint64_t max)
+uint64_t pseudorand_(uint64_t max, uint64_t *offset)
{
init_if_needed();
assert(max);
+
+ /* We try to avoid being order-dependent here. */
+ if (randbytes_overridden()) {
+ uint64_t rand;
+ randbytes_(&rand, sizeof(rand), offset);
+ return rand % max;
+ }
return isaac64_next_uint(&isaac64, max);
}
-uint64_t pseudorand_u64(void)
+uint64_t pseudorand_u64_(uint64_t *offset)
{
init_if_needed();
+ if (randbytes_overridden()) {
+ uint64_t rand;
+ randbytes_(&rand, sizeof(rand), offset);
+ return rand;
+ }
return isaac64_next_uint64(&isaac64);
}
-double pseudorand_double(void)
+double pseudorand_double_(uint64_t *offset)
{
init_if_needed();
+ if (randbytes_overridden()) {
+ uint64_t rand;
+ randbytes_(&rand, sizeof(rand), offset);
+ return rand / (double)UINT64_MAX;
+ }
return isaac64_next_double(&isaac64);
}
diff --git a/common/pseudorand.h b/common/pseudorand.h
index ebe9e0b5..0e5ce6c5 100644
--- a/common/pseudorand.h
+++ b/common/pseudorand.h
@@ -8,18 +8,21 @@
/**
* pseudorand - pseudo (guessable!) random number between 0 and max-1.
*/
-uint64_t pseudorand(uint64_t max);
+#define pseudorand(max) ({static uint64_t offset; pseudorand_((max), &offset);})
+uint64_t pseudorand_(uint64_t max, uint64_t *offset);
/**
- * pseudorand - pseudo (guessable!) random number between 0 and UINT64_MAX.
+ * pseudorand_u64 - pseudo (guessable!) random number between 0 and UINT64_MAX.
*/
-uint64_t pseudorand_u64(void);
+#define pseudorand_u64() ({static uint64_t offset; pseudorand_u64_(&offset);})
+uint64_t pseudorand_u64_(uint64_t *offset);
/**
* pseudorand - pseudo (guessable!) random number between 0 (inclusive) and 1
* (exclusive).
*/
-double pseudorand_double(void);
+#define pseudorand_double() ({static uint64_t offset; pseudorand_double_(&offset);})
+double pseudorand_double_(uint64_t *offset);
/**
* Get the siphash seed for hash tables.
diff --git a/common/test/run-psbt_diff.c b/common/test/run-psbt_diff.c
index 235163fe..1c107851 100644
--- a/common/test/run-psbt_diff.c
+++ b/common/test/run-psbt_diff.c
@@ -38,9 +38,9 @@ u8 fromwire_u8(const u8 **cursor UNNEEDED, size_t *max UNNEEDED)
/* Generated stub for fromwire_u8_array */
void fromwire_u8_array(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, u8 *arr UNNEEDED, size_t num UNNEEDED)
{ fprintf(stderr, "fromwire_u8_array called!\n"); abort(); }
-/* Generated stub for pseudorand_u64 */
-uint64_t pseudorand_u64(void)
-{ fprintf(stderr, "pseudorand_u64 called!\n"); abort(); }
+/* Generated stub for pseudorand_u64_ */
+uint64_t pseudorand_u64_(uint64_t *offset UNNEEDED)
+{ fprintf(stderr, "pseudorand_u64_ called!\n"); abort(); }
/* Generated stub for towire */
void towire(u8 **pptr UNNEEDED, const void *data UNNEEDED, size_t len UNNEEDED)
{ fprintf(stderr, "towire called!\n"); abort(); }
diff --git a/onchaind/test/run-grind_feerate-bug.c b/onchaind/test/run-grind_feerate-bug.c
index 4e64c268..e57aca2a 100644
--- a/onchaind/test/run-grind_feerate-bug.c
+++ b/onchaind/test/run-grind_feerate-bug.c
@@ -175,6 +175,9 @@ void peer_billboard(bool perm UNNEEDED, const char *fmt UNNEEDED, ...)
/* Generated stub for randbytes_ */
void randbytes_(void *bytes UNNEEDED, size_t num_bytes UNNEEDED, u64 *offset UNNEEDED)
{ fprintf(stderr, "randbytes_ called!\n"); abort(); }
+/* Generated stub for randbytes_overridden */
+bool randbytes_overridden(void)
+{ fprintf(stderr, "randbytes_overridden called!\n"); abort(); }
/* Generated stub for shachain_get_secret */
bool shachain_get_secret(const struct shachain *shachain UNNEEDED,
u64 commit_num UNNEEDED,
diff --git a/tests/fuzz/libfuzz.c b/tests/fuzz/libfuzz.c
index aada6f8c..00e4b84d 100644
--- a/tests/fuzz/libfuzz.c
+++ b/tests/fuzz/libfuzz.c
@@ -21,7 +21,7 @@ int LLVMFuzzerInitialize(int *argc, char ***argv);
/* Provide a non-random pseudo-random function to speed fuzzing. */
static isaac64_ctx isaac64;
-uint64_t pseudorand(uint64_t max)
+uint64_t pseudorand_(uint64_t max, uint64_t *offset)
{
assert(max);
return isaac64_next_uint(&isaac64, max);
diff --git a/wire/test/run-peer-wire.c b/wire/test/run-peer-wire.c
index a220af8f..cc688645 100644
--- a/wire/test/run-peer-wire.c
+++ b/wire/test/run-peer-wire.c
@@ -25,6 +25,9 @@ extern secp256k1_context *secp256k1_ctx;
/* Generated stub for randbytes_ */
void randbytes_(void *bytes UNNEEDED, size_t num_bytes UNNEEDED, u64 *offset UNNEEDED)
{ fprintf(stderr, "randbytes_ called!\n"); abort(); }
+/* Generated stub for randbytes_overridden */
+bool randbytes_overridden(void)
+{ fprintf(stderr, "randbytes_overridden called!\n"); abort(); }
/* AUTOGENERATED MOCKS END */
/* memsetting pubkeys doesn't work */
diff --git a/wire/test/run-tlvstream.c b/wire/test/run-tlvstream.c
index 3782191d..5aa60933 100644
--- a/wire/test/run-tlvstream.c
+++ b/wire/test/run-tlvstream.c
@@ -33,6 +33,9 @@ bool fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED,
/* Generated stub for randbytes_ */
void randbytes_(void *bytes UNNEEDED, size_t num_bytes UNNEEDED, u64 *offset UNNEEDED)
{ fprintf(stderr, "randbytes_ called!\n"); abort(); }
+/* Generated stub for randbytes_overridden */
+bool randbytes_overridden(void)
+{ fprintf(stderr, "randbytes_overridden called!\n"); abort(); }
/* Generated stub for towire_channel_id */
void towire_channel_id(u8 **pptr UNNEEDED, const struct channel_id *channel_id UNNEEDED)
{ fprintf(stderr, "towire_channel_id called!\n"); abort(); }
Why this scored 18/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.