
Linux Kernel's `/dev/random`: A Surprising Source of Latency and a Potential DoS Vector
Key Takeaways
Linux’s /dev/random can block applications and the system when its entropy pool is exhausted, turning a security feature into a performance and DoS issue. Consider /dev/urandom or hardware RNGs for predictable randomness.
- /dev/random’s blocking behavior is a deliberate security feature, but can become a performance bottleneck.
- Understanding entropy pool depletion is critical for high-performance, security-sensitive applications.
- Alternatives like /dev/urandom or hardware RNGs should be considered for predictable performance.
The Persistent Myth of /dev/random and the Hidden DoS
For years, many engineers have treated /dev/random as the sacred wellspring of cryptographic purity in Linux. The random(4) man page, unchanged in spirit for decades, dutifully warned of its potential to block indefinitely, a necessary evil for generating truly unpredictable random numbers. This led to a widely held belief: if you need strong randomness, you must use /dev/random, and if it hangs, your system has an entropy problem. The reality, however, is that for any reasonably modern Linux kernel (since v5.6, and largely since v4.8), this warning is a ghost. What remains is a potent legacy misconception that not only leads to unnecessary debugging but can, in specific scenarios, still expose systems to a subtle denial-of-service. This isn’t about a new bug; it’s about an old mechanism whose behavior has evolved significantly, while the collective understanding has lagged dangerously behind.
The Entropy Pool: More Than Just Noise
The Linux kernel’s approach to generating random numbers hinges on its entropy pool. This pool is populated by “environmental noise” – unpredictable hardware events like interrupt timings from disk I/O, keyboard presses, mouse movements, and even thermal noise in electronic components. The kernel estimates the amount of “true” randomness (entropy) gathered in this pool.
Historically, two primary interfaces provided access to this entropy: /dev/random and /dev/urandom. The critical distinction, and the source of much confusion, lay in their blocking behavior. /dev/random was designed to be the purist’s choice: it would only return random bytes if the kernel’s entropy estimation indicated a sufficient amount of “true” entropy was available. If the pool ran dry, /dev/random would block, waiting for more environmental noise to be collected. This behavior was intended for generating high-value cryptographic keys where unpredictability was paramount.
/dev/urandom, conversely, was the workhorse. It employed a Cryptographically Secure Pseudo-Random Number Generator (CSPRNG). This CSPRNG is seeded by the entropy pool, but once seeded, it can produce an effectively infinite stream of pseudo-random bytes, even if the entropy pool is temporarily depleted. The output is computationally indistinguishable from true randomness, making it suitable for almost all applications, including most cryptographic operations. The trade-off: it doesn’t guarantee “true” randomness in the same way /dev/random aims to, but in practice, it’s more than sufficient.
The introduction of the getrandom(2) system call in Linux kernel 3.17 was intended to simplify and modernize this interaction. It provides a file descriptor-less interface and allows explicit control via flags: GRND_RANDOM for blocking behavior (emulating the old /dev/random) and GRND_NONBLOCK for non-blocking (emulating /dev/urandom). However, the legacy interfaces and their associated myths persist.
The Kernel’s Evolution: From Blocking to Non-Blocking Default
The critical shift in the kernel’s random number generation behavior occurred gradually, with significant changes between kernel 4.8 and 5.6. Prior to these versions, the distinction between /dev/random and /dev/urandom was more pronounced, and /dev/random’s blocking nature was a real concern on systems with limited hardware entropy sources.
- Kernel 4.8: This version saw the CSPRNG switch to ChaCha20, a robust and performant algorithm. Importantly, this is also when the kernel began unifying the blocking and non-blocking entropy pools. The impact was that
/dev/randomstarted behaving more like/dev/urandomin many scenarios. - Kernel 5.6 (Released 2020): This release solidified the modern behavior. For all intents and purposes,
/dev/randomonly blocks during the very early stages of system initialization, before the CSPRNG has been adequately seeded. Once the system is up and running,/dev/randombehaves identically to/dev/urandom. TheO_NONBLOCKflag, which was historically relevant, is now largely ignored for/dev/randomoutside of this initial boot phase.
This means that on any Linux system running kernel 5.6 or later, reading from /dev/random will not cause an indefinite block due to entropy depletion. The kernel’s internal mechanisms ensure that even if the raw entropy pool is low, the CSPRNG can still provide a steady stream of pseudo-random data.
The Persistence of Myth and Its Consequences
Despite these kernel advancements, the old guard – outdated documentation and ingrained developer habits – continues to propagate the idea that /dev/random is inherently superior and perpetually blocking. This leads to several practical problems:
- Misdiagnosed Latency: Developers encountering application hangs on older systems, or in environments with genuinely low entropy during early boot, might blame
/dev/randomdirectly. On modern kernels, however, a hang during random number generation points to a different problem entirely – perhaps a bug in the application’s interaction withgetrandom(2)or a resource contention issue elsewhere. The belief in/dev/random’s constant blocking nature can send engineers down the wrong debugging path. - Unnecessary Complexity: Some systems might defensively install entropy-gathering daemons like
rngdorhavegedspecifically to feed/dev/random. While these tools are invaluable on older kernels or in highly constrained environments (like certain cloud VM images or embedded systems without diverse hardware event sources), they are often redundant on systems running newer kernels. This adds unnecessary complexity and potential attack surface. - The Residual DoS Vector: The true DoS risk with
/dev/randomon modern kernels is now largely confined to the early boot phase. If a critical service or microservice attempts to read a large amount of data from/dev/randomimmediately after kernel initialization, before the CSPRNG is fully seeded, it could potentially cause that service to block. If this service is essential for other processes to start or function, it can cascade into a system-wide hang or outage. This is a race condition, not a constant threat, but one that becomes more probable in environments with rapid service startup sequences or limited early entropy. Consider a cloud instance booting up, with multiple containerized services vying for initial random data; a poorly written service could starve others.
Under the Hood: How the Kernel Manages Entropy Now
The kernel’s internal RNG, accessible via getrandom(2), is a sophisticated piece of engineering. When you call getrandom(..., GRND_RANDOM, ...) on a modern kernel, the kernel doesn’t just stare at the entropy pool. It first checks if the CSPRNG, typically a ChaCha20 stream cipher initialized with a seed derived from the entropy pool, is ready. If it is, the function returns bytes generated by the CSPRNG. If the CSPRNG is not yet initialized (i.e., during early boot), then it will block, waiting for sufficient entropy to seed the CSPRNG.
The entropy pool itself is constantly being replenished. The kernel monitors sources like /dev/input/event*, hardware RNGs (like Intel’s RDRAND/RDSEED), and other interrupt sources. Even if the entropy count in /proc/sys/kernel/random/entropy_avail appears low, the kernel can still function because the CSPRNG acts as a high-quality entropy expander. The entropy pool is primarily for seeding and re-seeding, not for every byte of random data requested.
For example, to check your system’s current entropy level, you can use:
cat /proc/sys/kernel/random/entropy_avail
On a busy server with typical hardware, this number might fluctuate between a few hundred and a couple of thousand bits. On a minimal cloud VM or container without much I/O, it might be significantly lower. However, this value alone does not dictate whether /dev/random or getrandom(..., GRND_RANDOM) will block on a modern kernel.
Bonus Perspective: The Hardware RNG Vulnerability
While we’ve focused on software misinterpretations, hardware RNGs themselves are not immune to flaws. The reported vulnerability in AMD’s Zen 5 CPUs (CVE-2025-62626), where RDSEED could return predictable zero values under certain conditions, underscores a critical point: relying solely on a single hardware source for entropy, even for seeding a CSPRNG, carries risk. Kernel developers must continuously evaluate and patch their RNG implementations to account for such hardware-level defects. For affected systems, the mitigation might involve explicitly disabling vulnerable hardware RNGs or implementing sanity checks on their output, further highlighting the complexity of achieving true randomness at scale. This contrasts sharply with the simple “use /dev/random” advice, which assumes perfect underlying hardware.
Opinionated Verdict: Embrace getrandom(2) and Shed the Old Dogma
The days of treating /dev/random as a perpetually blocking entity on modern Linux kernels are over. For virtually all applications, getrandom(2) with the GRND_NONBLOCK flag (or its historical equivalent, reading from /dev/urandom) is the correct and performant choice. This provides cryptographically secure pseudo-random numbers without the risk of application hangs due to low entropy.
If your application absolutely must have the theoretical guarantee of blocking until a higher level of entropy is met (a requirement so rare it’s usually a sign of misunderstanding), use getrandom(2) with the GRND_RANDOM flag. Be acutely aware that on older kernels or during the critical initial boot phase on any kernel, this can lead to blocking and, in a distributed system or a microservice startup, can become a DoS vector.
The responsibility now lies with practitioners to update their understanding. Rely on getrandom(2), understand its flags, and leverage the fact that the Linux kernel has evolved significantly beyond the simplistic binary of “blocking good, non-blocking bad.” The real failure mode isn’t the kernel’s RNG; it’s the persistent dogma that prevents us from using it correctly.




