common: don't consume deterministic randbytes stream for trace ids
What changed, and why it matters
This change fixes a subtle bug in Core Lightning's tracing code. When running with a special developer-only deterministic randomness override (CLN_DEV_ENTROPY_SEED), trace IDs were being drawn from the same predictable random stream used for actual transaction details like PSBT serial IDs and locktime fuzzing. Because tracing may be enabled or disabled depending on build settings, this meant the same test or operation could produce different transaction outputs depending on whether tracing was compiled in. The fix makes trace IDs use a separate simple counter when deterministic mode is active, so tracing no longer shifts the random stream and transaction results stay consistent.
Treat as a low-severity correctness/reproducibility fix. No immediate production security response is required because CLN_DEV_ENTROPY_SEED is a developer/testing-only override. Review whether any other observability or diagnostic code paths consume pseudorand_u64() under deterministic mode and could similarly perturb reproducible transaction generation or fuzzing.
Security signals we found
Deterministic RNG stream contamination by observability code
Transaction output divergence between instrumented and non-instrumented builds
Developer-only entropy override affecting reproducibility of transaction generation
Potential for non-deterministic test failures or inconsistent fuzzing/coverage results
Evidence from the diff
The commit introduces trace_rand_u64() in common/trace.c. When randbytes_overridden() is true (i.e., CLN_DEV_ENTROPY_SEED is active), trace/span IDs are generated from a static u64 counter instead of pseudorand_u64(). Otherwise, they still use pseudorand_u64(). This prevents tracing instrumentation from consuming bytes from the deterministic RNG stream, which was causing divergence between builds with and without HAVE_USDT/CLN_DEV_TRACE_FILE/CLN_TRACE_SOCKET for downstream consumers such as PSBT serial IDs and locktime fuzzing.
Changed components
common/trace.cTracing/span initialization (init_span)Deterministic RNG override path (CLN_DEV_ENTROPY_SEED)Builds with USDT/tracing enabled (HAVE_USDT, CLN_DEV_TRACE_FILE, CLN_TRACE_SOCKET)Inspect captured patch +18 / −3
diff --git a/common/trace.c b/common/trace.c
index bfea980..fdd6205 100644
--- a/common/trace.c
+++ b/common/trace.c
@@ -10,6 +10,7 @@
#include <common/json_stream.h>
#include <common/memleak.h>
#include <common/pseudorand.h>
+#include <common/randbytes.h>
#include <common/trace.h>
#include <fcntl.h>
#include <inttypes.h>
@@ -104,6 +105,20 @@ static struct span fixed_spans[8];
static struct span_htable *spans = NULL;
static struct span *current;
+/* Span/trace ids are observability metadata: when the deterministic
+ * RNG override is active (CLN_DEV_ENTROPY_SEED), don't draw them from
+ * the deterministic stream, or builds with tracing active (HAVE_USDT,
+ * CLN_DEV_TRACE_FILE, CLN_TRACE_SOCKET) shift every later draw and
+ * produce different transactions than builds without. */
+static u64 trace_rand_u64(void)
+{
+ static u64 counter;
+
+ if (randbytes_overridden())
+ return ++counter;
+ return pseudorand_u64();
+}
+
static void init_span(struct span *s, size_t key, const char *name,
struct span *parent)
{
@@ -111,7 +126,7 @@ static void init_span(struct span *s, size_t key, const char *name,
time_now(); /* discouraged: but tracing wants non-dev time */
s->key = key;
- s->id = pseudorand_u64();
+ s->id = trace_rand_u64();
s->start_time = (now.ts.tv_sec * 1000000) + now.ts.tv_nsec / 1000;
s->parent = parent;
s->name = name;
@@ -122,8 +137,8 @@ static void init_span(struct span *s, size_t key, const char *name,
/* If this is a new root span we also need to associate a new
* trace_id with it. */
if (!s->parent) {
- s->trace_id_hi = pseudorand_u64();
- s->trace_id_lo = pseudorand_u64();
+ s->trace_id_hi = trace_rand_u64();
+ s->trace_id_lo = trace_rand_u64();
} else {
s->trace_id_hi = current->trace_id_hi;
s->trace_id_lo = current->trace_id_lo;
Why this scored 37/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.