Repository navigation
Data loss: sub-agent TemporaryDirectory cleanup traversed an active bind mount and deleted the host repository #33557
Description
What version of Codex CLI is running?
codex-cli 0.144.3
What subscription do you have?
ChatGPT Pro 20x
Which model were you using?
gpt-5.6-sol, xhigh reasoning effort
What platform is your computer?
Linux (Kubuntu, ext4 filesystem; exact uname output unavailable from the preserved incident environment)
What terminal emulator and version are you using (if applicable)?
Codex CLI/TUI; terminal emulator and version unavailable from the preserved incident environment
Codex doctor report
Not available from the preserved incident environment. The installed system was shut down for evidence preservation and offline recovery.What issue are you seeing?
Summary
A delegated Codex sub-agent deleted contents from a real local repository
during a host-only adversarial review. The delegated task explicitly said
READ-ONLY ADVERSARIAL REVIEW and Do not edit. Repository deletion was not
requested by the operator.
The sub-agent created a Python TemporaryDirectory, bind-mounted the real
repository below it, and did not install a guaranteed unmount path. An
unhandled exception then caused TemporaryDirectory.__exit__() to run
recursive cleanup while the bind mount was still active. Cleanup traversed the
mount and deleted contents from the real repository.
Environment
- Codex CLI:
0.144.3 - Interface: Codex CLI/TUI
- Model:
gpt-5.6-sol - Reasoning effort:
xhigh - OS: Linux/Kubuntu, ext4
- Sandbox:
danger-full-access - Approval policy:
never - Permission profile: disabled
- Incident time: 2026-07-14 approximately 04:14:30 KST (UTC+09:00)
Failure sequence
real repository
-> bind-mounted below Python TemporaryDirectory
-> bind mount remained active
-> an unhandled ValueError occurred
-> TemporaryDirectory.__exit__ invoked cleanup
-> shutil.rmtree traversed the active bind mount
-> contents in the real repository were deleted
The retained tool log contains the direct traceback:
ValueError
-> TemporaryDirectory.__exit__
-> cleanup
-> shutil.rmtree
-> _rmtree_safe_fd_step
-> PermissionError inside the mounted repository tree
The final PermissionError stopped cleanup only after it had already traversed
the mounted repository. Subsequent filesystem inspection found major repository
content missing.
Impact
Affected path, with the username redacted:
/home/<user>/dev/A90_5G_rooting
Observed missing content included:
.gittestsdocsworkspace/privateAGENTS.mdGOAL.md
Only a partial workspace tree remained locally.
The incident required stopping normal use of the installed Linux system,
booting a Live USB, keeping the source ext4 partition unmounted and
block-device read-only, and creating a complete 255743810560-byte partition
image with GNU ddrescue. Imaging completed with 100% rescued and zero physical
read errors. Logical undelete and exact reconstruction remain separate work.
This was a host-filesystem incident. No phone, ADB, Odin, or device-flashing
operation occurred.
What steps can reproduce the bug?
This must be reproduced only with a disposable filesystem image or disposable
test tree, never with a real workspace.
- Start Codex CLI with a disposable repository or disposable test tree as its working directory.
- Delegate a nominally read-only adversarial task to a sub-agent.
- In a private user/mount namespace, create a Python
TemporaryDirectory. - Create a child directory below it and bind-mount a disposable repository or
test tree onto that child directory. - Do not unmount the bind in a guaranteed
finallyblock. - Raise an exception inside the
TemporaryDirectorycontext. - Observe whether automatic cleanup crosses the active mount boundary and
removes files from the mounted disposable tree.
Sanitized shape of the incident-producing code:
with tempfile.TemporaryDirectory(prefix="mount-cleanup-probe-") as name:
temporary = Path(name)
mounted_repo = temporary / "repo"
mounted_repo.mkdir()
mount_bind(disposable_repo, mounted_repo)
# An exception occurred here.
# There was no guaranteed unmount before leaving the context.The important condition is that TemporaryDirectory cleanup runs while the
descendant bind mount remains active.
What is the expected behavior?
Codex should not generate or run a destructive mount-namespace probe against a
real workspace when the delegated task is explicitly read-only.
At minimum, agent and runtime safety controls should detect or prevent this
pattern:
- a real workspace bind-mounted below an automatically cleaned temporary tree
- recursive cleanup while the target or a descendant is an active mountpoint
- mount creation without a guaranteed unmount in
finally - destructive testing against a real repository instead of a disposable image
- execution of filesystem-mutating commands after an explicit
Do not edit
instruction
A safe harness should:
- use a disposable filesystem image or disposable copied tree
- track every mount and unmount it in guaranteed cleanup
- inspect
/proc/self/mountinfobefore any recursive deletion - refuse deletion when the cleanup target or any descendant is a mountpoint
- require explicit confirmation before a test can expose a real workspace to
recursive cleanup, even underdanger-full-accessand approval policy
never
Additional information
Related issues — not exact duplicates
- Severe Codex Safety Failure: Docker Container / Host Filesystem Boundary Confusion Risked 31 Years of Archived Client Data #24501 — container/host filesystem
namespace confusion followed by destructive deletion advice; different
mechanism - Linux sandbox leaks empty .git/.agents/.codex dirs into /tmp when the helper is killed, and later runs never reclaim them #31599 — Linux sandbox synthetic mount
cleanup leak; same broad mount/cleanup family, but no recursive repository
deletion - Codex VS Code extension/agent ran git restore despite explicit “never touch git” instruction, overwriting uncommitted work (data loss) #8643 — unrequested destructive Git
operation causing data loss; different mechanism
I searched the issue tracker for combinations of bind mount,
TemporaryDirectory, recursive cleanup, mountpoint, sub-agent deleted repository, and data loss. I did not find an existing issue describing this
exact failure mechanism: recursive cleanup traversing an active bind mount below
a temporary directory.
Evidence availability
The incident-producing session log was preserved outside the deleted
repository. Its SHA-256 matches the contemporaneous incident handoff. The log
contains the session metadata, exact tool call, timestamp, generated probe, and
cleanup traceback.
Session identifiers, the log hash, sanitized command excerpts, and recovery
records can be provided privately to OpenAI Support. Full session logs, private
repository contents, and the raw filesystem image will not be posted publicly.
Activity
Temmie-Tem commentedon Jul 16, 2026
Additional scope and evidence clarification
I reviewed the preserved incident record and am adding only sanitized, high-level facts here:
sudoor obtain host-root privileges. It placed a writable bind of the live repository below a PythonTemporaryDirectorywithout a read-only remount, an unmount call, or afinallycleanup boundary.TemporaryDirectorycleanup to run before namespace process exit. Recursive cleanup entered the active repository bind; later permission errors stopped cleanup only after traversal had begun.This is not being reported as a sandbox escape or host privilege escalation. It is an unsafe cleanup/lifetime pattern applied to a live repository during an explicitly read-only delegated task. Exact prompts, paths, session identifiers, and log excerpts are being kept private and can be provided to OpenAI through an approved private support channel.