Bug #22110
openRuby::Box C-extension loading can be denied by predictable temporary filename
Description
Ruby::Box is experimental, but I found a reproducible local hardening issue in its native extension loading path.
When a C extension is required inside a Ruby::Box, Ruby copies the .so to a temporary filename derived from predictable values:
tmpdir + "_ruby_box_" + "p" + getpid() + "_" + box_id + "_" + escaped_basename
Because the filename has no random component, another same-host local user can pre-create the path before the victim process loads the extension. The victim’s open(..., O_CREAT|O_EXCL, ...) call then fails, and require raises LoadError.
This does not allow code injection. O_CREAT|O_EXCL correctly rejects pre-existing files and symlinks. The impact is only denial of Ruby::Box C-extension loading.
Environment:
Ruby 4.1.0-dev @ 533abc7ded
Ruby::Box enabled with RUBY_BOX=1
Linux x86_64
Reproduction:
From a Ruby build tree with ./ruby available:
RUBY=./ruby sh security_review_claude_opus48_max_20260613_050521/poc/box_dos_attack.sh
Observed:
The attacker process discovers the victim PID, pre-creates likely paths under /tmp, and the victim fails with:
LoadError: can't prepare the extension file for Ruby Box (.../_ruby_box_p<pid>_4_fcntl.so from .../fcntl.so): can't open the file to write
Control:
Without pre-creating the path, the same Ruby::Box extension require succeeds.
Expected:
A third-party same-host user should not be able to deny Ruby::Box C-extension loading by pre-creating a predictable temp filename.
Suggested fix:
Use an unpredictable temporary filename, for example mkstemp or a random suffix, preferably inside a private per-process directory with 0700 permissions. The copied extension file can also use mode 0600 instead of mirroring the source .so permissions.
Notes:
This is not a sandbox escape, not code execution, not privilege escalation, and not information disclosure. It is a local denial/hardening issue for an experimental opt-in feature.
Files
Updated by mame (Yusuke Endoh) 2 days ago
- Status changed from Open to Assigned
- Assignee set to tagomoris (Satoshi Tagomori)