fix: make shm cache files owner-private — they leak/accept pickle data (#39)#82
Merged
Conversation
The mmap cache files were created in a shared directory (/dev/shm on Linux, $TMPDIR/warp_cache otherwise) with default umask permissions (typically 0644, world-readable) and predictable names derived from module+qualname. On a multi-user host any local user could read another user's cache — which holds serialized (and possibly pickled) return values — or, worse, pre-create the predictably-named file with a crafted pickle payload that the victim then feeds to pickle.loads (the deserialize fallback), i.e. potential code execution. Harden the shm layout: - Store files in a per-user directory `warp_cache-<uid>` created 0o700 (under /dev/shm on Linux, $TMPDIR otherwise). The 0o700 directory is what actually closes both vectors: other users can neither read files in it nor create files in it. - Create every cache file (.data/.lock/.init) with mode 0o600 as defense in depth. - `ensure_secure_dir` refuses to use a pre-existing directory that is not a directory, is owned by another user, or is group/other-accessible (a hostile user could pre-create our uid-scoped dir on the world-writable, sticky /dev/shm); it tightens loose perms on a dir we own. Uses symlink_metadata so a symlink can't redirect us elsewhere. Add a regression test asserting the per-user dir is 0o700 and every shm file is owner-only (mode & 0o077 == 0); verified fail-before -> pass-after. Update the test shm-dir helpers for the new per-user layout, and document the invariant in ARCHITECTURE. The shm module is non-Windows only, so the unix-specific perms code always applies where the shared backend exists. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.
Closes #39
What & why
The mmap cache files were created in a shared directory (
/dev/shmon Linux,$TMPDIR/warp_cacheotherwise) with default umask permissions (typically0644, world-readable) and predictable names (warp_cache_<module>_<qualname>_<hash>). On a multi-user host (shared server, CI runner, shared-tmp container) this is a local trust-boundary issue:create_or_openopens a pre-existing file if the magic/version/sizes match (all forgeable), anddeserialize_valuefalls back topickle.loadson the bytes — so the victim deserializes attacker-controlled data.The fix
warp_cache-<uid>created0o700(under/dev/shmon Linux,$TMPDIRotherwise). This0o700dir is what actually closes both vectors — other users can neither read files in it nor create files in it..data/.lock/.init) created0o600viaOpenOptionsExt::mode, as defense in depth (and so the result is deterministic regardless of umask).ensure_secure_dirrefuses to use a pre-existing dir that isn't a directory, is owned by another user, or is group/other-accessible — defending the/dev/shm(world-writable, sticky) pre-creation/TOCTOU case where an attacker pre-creates our uid-scoped dir. It usessymlink_metadataso a symlink can't redirect us, and tightens loose perms on a dir we own.The shm module is non-Windows only, so the unix-specific permission code always applies where the shared backend exists.
Test
TestSharedFilePermissions::test_shm_dir_and_files_are_owner_only— after creating a shared cache, asserts the per-user dir is0o700and every shm file has no group/other bits (mode & 0o077 == 0).Verified fail-before → pass-after by stashing just the
region.rschange: the pre-fix build doesn't place files in the secure per-user dir (and creates them0o644); with the fix the dir is0o700and files are0o600.Test shm-dir helpers across the suite are updated for the new per-user layout (with a Windows guard where a helper is collected on Windows).
Follow-up
This fixes the file-permission half of the shared-backend risk. The pickle-deserialization documentation gap is tracked separately in #46 (SECURITY.md); the
0o700dir already removes the realistic tampering path for the pickle vector here.Gates run (risky —
src/shm/region.rs, security/file-I/O)make fmt/make lint(ruff, ty, clippy-D warnings) ✓make test—cargo test(11) + pytest (107, +1 new) ✓make test-matrix— Python 3.10–3.13 ✓, the new test + cross-process cold-start test pass on each (3.14 skipped locally via the documenteduv-resolves-stale-alpha guard; CI covers 3.14 final)make bench— no regression (construction-time-only change): shared backend ~9.5M ops/s, hit-rate 72.9%🤖 Generated with Claude Code