feat(rand): add DD_TRACE_SECURE_RANDOM support to span ID generation#3873
Merged
bwoebi merged 2 commits intoMay 15, 2026
Merged
Conversation
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) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ad49f5d1d2
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
bwoebi
approved these changes
May 13, 2026
|
🎯 Code Coverage (details) 🔗 Commit SHA: 45ba56e | Docs | Datadog PR Page | Give us feedback! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Tech Doc
https://datadoghq.atlassian.net/browse/SVLS-9142
Background
For Firecracker-based container technology, in order to reduce cold-start latency, the system snapshots the entire process memory of a warmed-up instance and reuses it to launch new ones. Every resumed instance starts from the same frozen memory image — including any userspace PRNG state that was initialized before the snapshot was taken.
Motivation
Standard PRNGs seed once at startup and produce a deterministic sequence from that point forward. When Firecracker resumes thousands of instances from the same snapshot, every one of them begins at the same position in that sequence. Concurrent instances then generate identical trace IDs and span IDs, corrupting distributed traces and making sampled data statistically meaningless.
The fix cannot live inside each language tracer: from inside a resumed process, a cold start and a snapshot restore are indistinguishable. The process simply wakes up already initialized — no changed PID, no kernel signal, no env var that differs between the two cases.
Solution
serverless-init (PID 1) is the only process that knows, before exec, that the child will run in a Firecracker snapshot environment. It injects DD_TRACE_SECURE_RANDOM=true into the child's environment before launch. Each tracer reads this flag at startup and switches ID generation to draw directly from the kernel entropy pool on every call (getrandom(2)). With no userspace PRNG state to freeze, every resumed instance generates an independent sequence — regardless of when the snapshot was taken.
Summary
DD_TRACE_SECURE_RANDOMboolean config option (defaultfalse) toext/configuration.hDD_TRACE_SECURE_RANDOM=true,ddtrace_generate_span_id()callsphp_random_bytes_silent()— which reads from the OS entropy pool (getrandom(2)) per call — instead of the MT19937-64 PRNGddtrace_seed_prng()RINIT seeding are fully preserved for non-secure-random deploymentsMotivation
PHP's span ID generator uses MT19937-64, a Mersenne Twister whose
312-element state array lives as a
staticC variable inddtrace.so.The state is seeded at RINIT (per PHP request) from
php_random_int_silent()which is cryptographically secure, but the derived PRNG state is
captured verbatim in any process memory snapshot.
In process-snapshot environments, instances restored from the same
snapshot have identical MT state and thus produce the same span ID
sequence. When
DD_TRACE_SECURE_RANDOM=true, the MT is bypassedentirely:
php_random_bytes_silent()callsgetrandom(2)directlyon each invocation with no userspace PRNG state, so snapshot-restored
instances draw independent entropy immediately.
This flag is intended to be injected automatically by the serverless
init container process before spawning the user application.
Test plan
tests/ext/secure_random_generates_nonzero_ids.phpt— verifies non-zero,distinct IDs under the
DD_TRACE_SECURE_RANDOM=truepathtests/ext/secure_random_ignores_prng_seed.phpt— verifies that a fixedDD_TRACE_DEBUG_PRNG_SEEDdoes not constrain output whenDD_TRACE_SECURE_RANDOM=true(proving the MT is fully bypassed)Compatibility
php_random_bytes_silentis available via existing conditional includesin
ext/random.c—ext/standard/php_random.h(PHP < 8.4) andext/random/php_random.h(PHP ≥ 8.4) — no new header dependencies.🤖 Generated with Claude Code