Skip to content

Commit 09d8943

Browse files
Add DD_TRACE_SECURE_RANDOM support to span ID generation (#3873)
When DD_TRACE_SECURE_RANDOM=true, ddtrace_generate_span_id() bypasses the MT19937-64 thread-local state and calls php_random_bytes_silent() instead, which reads from the OS entropy pool (getrandom(2)) on every invocation with no userspace PRNG state. This ensures span IDs are drawn from the kernel entropy pool on every call, making them safe in process-snapshot environments where PRNG state seeded at startup would be identical across all resumed instances. The existing MT path and ddtrace_seed_prng() RINIT seeding are unchanged; the 128-bit trace ID .time component is a Unix timestamp and requires no fix. Tests: secure_random_generates_nonzero_ids.phpt verifies non-zero distinct IDs under the CSPRNG path; secure_random_ignores_prng_seed.phpt verifies that a fixed DD_TRACE_DEBUG_PRNG_SEED does not constrain output when DD_TRACE_SECURE_RANDOM=true. Co-Authored-By: Claude Sonnet 4.6 (1M context) <[email protected]>
1 parent bb736d6 commit 09d8943

5 files changed

Lines changed: 84 additions & 1 deletion

File tree

ext/configuration.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ enum ddtrace_sidecar_connection_mode {
191191
CONFIG(INT, DD_TRACE_AGENT_CONNECT_TIMEOUT, DD_CFG_EXPSTR(DD_TRACE_AGENT_CONNECT_TIMEOUT_VAL), \
192192
.ini_change = zai_config_system_ini_change) \
193193
CONFIG(INT, DD_TRACE_DEBUG_PRNG_SEED, "-1", .ini_change = ddtrace_reseed_seed_change) \
194+
CONFIG(BOOL, DD_TRACE_SECURE_RANDOM, "false") \
194195
CONFIG(BOOL, DD_LOG_BACKTRACE, "false") \
195196
CONFIG(BOOL, DD_CRASHTRACKING_ENABLED, DD_CRASHTRACKING_ENABLED_DEFAULT) \
196197
CONFIG(BOOL, DD_TRACE_GENERATE_ROOT_SPAN, "true", .ini_change = ddtrace_span_alter_root_span_config) \

ext/random.c

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,15 @@ uint64_t ddtrace_parse_hex_span_id(zval *zid) {
106106
return ddtrace_parse_hex_span_id_str(Z_STRVAL_P(zid), Z_STRLEN_P(zid));
107107
}
108108

109-
uint64_t ddtrace_generate_span_id(void) { return (uint64_t)genrand64_int64(); }
109+
uint64_t ddtrace_generate_span_id(void) {
110+
if (get_DD_TRACE_SECURE_RANDOM()) {
111+
uint64_t id;
112+
if (php_random_bytes_silent(&id, sizeof(id)) == SUCCESS) {
113+
return id;
114+
}
115+
}
116+
return (uint64_t)genrand64_int64();
117+
}
110118

111119
uint64_t ddtrace_peek_span_id(void) {
112120
ddtrace_span_properties *pspan = DDTRACE_G(active_stack) ? DDTRACE_G(active_stack)->active : NULL;

metadata/supported-configurations.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2202,6 +2202,13 @@
22022202
"default": "glob"
22032203
}
22042204
],
2205+
"DD_TRACE_SECURE_RANDOM": [
2206+
{
2207+
"implementation": "A",
2208+
"type": "boolean",
2209+
"default": "false"
2210+
}
2211+
],
22052212
"DD_TRACE_SHUTDOWN_TIMEOUT": [
22062213
{
22072214
"implementation": "A",
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
--TEST--
2+
DD_TRACE_SECURE_RANDOM generates valid non-zero span IDs
3+
--ENV--
4+
DD_TRACE_GENERATE_ROOT_SPAN=false
5+
DD_TRACE_DEBUG=false
6+
DD_TRACE_SECURE_RANDOM=true
7+
--FILE--
8+
<?php
9+
10+
DDTrace\start_span();
11+
$id1 = DDTrace\active_span()->id;
12+
DDTrace\close_span();
13+
14+
DDTrace\start_span();
15+
$id2 = DDTrace\active_span()->id;
16+
DDTrace\close_span();
17+
18+
// Both IDs must be non-empty numeric strings representing non-zero values.
19+
var_dump(ctype_digit($id1) && $id1 !== '0');
20+
var_dump(ctype_digit($id2) && $id2 !== '0');
21+
22+
// Two consecutive IDs drawn from a CSPRNG must differ.
23+
var_dump($id1 !== $id2);
24+
25+
?>
26+
--EXPECT--
27+
bool(true)
28+
bool(true)
29+
bool(true)
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
--TEST--
2+
DD_TRACE_SECURE_RANDOM bypasses the deterministic PRNG seed
3+
--ENV--
4+
DD_TRACE_GENERATE_ROOT_SPAN=false
5+
DD_TRACE_DEBUG=false
6+
DD_TRACE_SECURE_RANDOM=true
7+
DD_TRACE_DEBUG_PRNG_SEED=42
8+
--FILE--
9+
<?php
10+
11+
// With DD_TRACE_DEBUG_PRNG_SEED set and DD_TRACE_SECURE_RANDOM=false,
12+
// the MT19937-64 produces a fixed sequence: every process run with the
13+
// same seed yields the same IDs in the same order.
14+
//
15+
// With DD_TRACE_SECURE_RANDOM=true the MT is bypassed entirely, so the
16+
// seed has no effect and consecutive IDs must differ (CSPRNG output).
17+
18+
DDTrace\start_span();
19+
$id1 = DDTrace\active_span()->id;
20+
DDTrace\close_span();
21+
22+
DDTrace\start_span();
23+
$id2 = DDTrace\active_span()->id;
24+
DDTrace\close_span();
25+
26+
// IDs must be valid non-zero numeric strings.
27+
var_dump(ctype_digit($id1) && $id1 !== '0');
28+
var_dump(ctype_digit($id2) && $id2 !== '0');
29+
30+
// CSPRNG output must not be equal across calls; this fails deterministically
31+
// under the MT because seed 42 would produce the same first two values every run.
32+
var_dump($id1 !== $id2);
33+
34+
?>
35+
--EXPECT--
36+
bool(true)
37+
bool(true)
38+
bool(true)

0 commit comments

Comments
 (0)