Skip to content

[Linux] Prefer getrandom(2) over /dev/urandom; keep /proc/self/statm open - #394

Open
dylan-conway wants to merge 1 commit into
mainfrom
claude/fs-sandbox-startup-reads
Open

[Linux] Prefer getrandom(2) over /dev/urandom; keep /proc/self/statm open#394
dylan-conway wants to merge 1 commit into
mainfrom
claude/fs-sandbox-startup-reads

Conversation

@dylan-conway

Copy link
Copy Markdown
Member

Two small Linux-only changes so JSC can start inside a filesystem sandbox that hides /dev and /proc without crashing, and does less work on a GC path. No behaviour change on macOS/Windows/FreeBSD.

WTF::RandomDevice and bmalloc::ARC4RandomNumberGenerator::stir

Today both open /dev/urandom at startup and CRASH() / RELEASE_BASSERT if that fails, then keep the fd for the life of the process.

Now, on Linux, each probes getrandom(…, GRND_NONBLOCK) once:

  • returns 1 → the kernel pool is initialized; use getrandom for every subsequent read. No path is touched and no fd is held.
  • anything else — EAGAIN (pool not yet initialized, where a /dev/urandom read would have returned immediately), ENOSYS (kernel < 3.17), EPERM (seccomp) → fall through to the existing /dev/urandom code, unchanged.

So the only observable difference is on systems where getrandom already works and would return the same bytes from the same pool: one fewer open file and no dependency on /dev. The fallback open() gains O_CLOEXEC.

WTF::currentProcessMemoryStatus()

Reached from Heap::proportionalHeapSizememoryFootprint() (because USE(BUN_JSC_ADDITIONS) turns on USE_MEMORY_FOOTPRINT_API), i.e. after collections. It did fopen("/proc/self/statm") + fgets + fclose on every call. It now opens the file once (O_RDONLY|O_CLOEXEC) and pread()s at offset 0, which is exactly what LinuxMemory in AvailableMemory.cpp already does for memoryStatus(). If the open fails it latches and keeps returning zeros as before, at zero cost.

Same caveat as LinuxMemory: the cached fd is bound to the pid that opened it, so a fork()-without-exec child would read the parent's numbers. JSCOnly never runs JSC in such a child (every fork is followed by exec), and upstream already accepted this trade-off for memoryStatus() on the same path.

Not built locally.

…open

Two small changes so a JSC embedder can start inside a filesystem
sandbox that hides /dev and /proc without crashing, and with fewer
syscalls on a GC path.

RandomDevice / bmalloc CryptoRandom:
  On Linux, probe getrandom(GRND_NONBLOCK) once. If the pool is already
  initialized, use getrandom for all subsequent reads and never open
  /dev/urandom (no path access, no fd held for the process lifetime).
  If the probe returns anything else -- EAGAIN (pool not yet ready,
  where /dev/urandom would have returned without blocking), ENOSYS
  (kernel < 3.17) or EPERM (seccomp) -- fall back to the existing
  /dev/urandom code unchanged, so behaviour is identical wherever
  getrandom is not a strict improvement. The urandom fd is now opened
  O_CLOEXEC.

currentProcessMemoryStatus():
  This is reached from Heap::proportionalHeapSize via memoryFootprint()
  after collections. Open /proc/self/statm once and pread() it instead
  of fopen/fgets/fclose per call, matching what LinuxMemory in
  AvailableMemory.cpp already does for memoryStatus(). If the open
  fails the function keeps returning zeros as before, but now costs
  nothing on later calls. As with LinuxMemory, the cached fd is bound
  to the opening pid; JSCOnly never runs JSC in a fork()-without-exec
  child.
@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 6642dac4-3971-4b23-8803-aee5f0e1a615

📥 Commits

Reviewing files that changed from the base of the PR and between 78d45d3 and 458db1d.

📒 Files selected for processing (3)
  • Source/WTF/wtf/RandomDevice.cpp
  • Source/WTF/wtf/linux/CurrentProcessMemoryStatus.cpp
  • Source/bmalloc/bmalloc/CryptoRandom.cpp

Walkthrough

Changes

Linux entropy source selection

Layer / File(s) Summary
getrandom detection and probing
Source/WTF/wtf/RandomDevice.cpp, Source/bmalloc/bmalloc/CryptoRandom.cpp
Linux builds define getrandom compatibility support. RandomDevice and CryptoRandom probe getrandom with GRND_NONBLOCK and select the fallback when unavailable.
Fallback descriptors and random-byte reads
Source/WTF/wtf/RandomDevice.cpp
Fallback descriptors use O_CLOEXEC. Destructors close valid descriptors. Random-byte reads select getrandom or /dev/urandom reads.

Process memory status collection

Layer / File(s) Summary
Persistent statm reads
Source/WTF/wtf/linux/CurrentProcessMemoryStatus.cpp
currentProcessMemoryStatus opens /proc/self/statm once, uses pread with EINTR retries, terminates the buffer explicitly, and preserves failure returns.
🚥 Pre-merge checks | ✅ 3 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description explains the changes and rationale, but it omits the required Bugzilla link, reviewer line, and formatted file or function list. Add the Bugzilla bug link, “Reviewed by NOBODY (OOPS!).” line, and the required path and function change entries.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the primary Linux changes: preferring getrandom(2) and caching /proc/self/statm.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Comment @coderabbitai help to get the list of available commands.

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I reviewed this PR and didn't find any bugs. Because it modifies the cryptographic randomness sources in both WTF and bmalloc (and the author notes it wasn't built locally), a human look would still be worthwhile.

What was reviewed:

  • Verified m_fd defaults to -1 in RandomDevice.h so the early-return path is sound; destructor now guards close().
  • Checked the getrandom probe/fallback: EAGAIN/ENOSYS/EPERM all fall through to the unchanged /dev/urandom path, and the read loop's existing EINTR/EAGAIN handling covers the blocking getrandom call.
  • Confirmed the statm open-once + pread pattern matches the existing LinuxMemory precedent in AvailableMemory.cpp; buffer is null-terminated with a byte reserved.
Extended reasoning...

Overview

Three Linux-only changes: (1) WTF::RandomDevice and (2) bmalloc::ARC4RandomNumberGenerator::stir now probe getrandom(2) with GRND_NONBLOCK at init and use it for all subsequent reads if the kernel entropy pool is initialized, falling back to the existing /dev/urandom path otherwise. (3) currentProcessMemoryStatus() switches from per-call fopen/fgets/fclose to a once-opened fd + pread, mirroring LinuxMemory::footprint() in AvailableMemory.cpp. All open() calls gain O_CLOEXEC.

Security risks

The first two changes touch the seeding path for cryptographic randomness — RandomDevice backs WTF::cryptographicallyRandomValues() and ARC4RandomNumberGenerator seeds bmalloc's CSPRNG. getrandom(2) without GRND_RANDOM draws from the same pool as /dev/urandom, so entropy quality is unchanged. The probe only commits to getrandom when it successfully returns 1 byte (pool initialized), and the pool cannot become uninitialized afterward, so subsequent blocking getrandom calls won't hang. The fallback path is byte-identical to the old code plus O_CLOEXEC. I don't see a way this weakens randomness, but any change to a CSPRNG seed source deserves a second set of eyes.

Level of scrutiny

High — this is security-critical infrastructure (crypto RNG seeding) even though the diff is small and mechanically straightforward. The author also notes "Not built locally", so CI is the first compile check.

Other factors

The statm change is a low-risk performance/robustness improvement with direct upstream precedent in the same tree. The fork-without-exec caveat is explicitly called out and matches what upstream already accepts for memoryStatus(). The #if guarding is careful (SYS_getrandom presence checked, GRND_NONBLOCK fallback-defined), and non-Linux platforms are unaffected by preprocessor construction.

@github-actions

github-actions Bot commented Aug 8, 2026

Copy link
Copy Markdown

Preview Builds

Commit Release Date
458db1d2 autobuild-preview-pr-394-458db1d2 2026-08-08 01:05:33 UTC

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant