Data loss: sub-agent TemporaryDirectory cleanup traversed an active bind mount and deleted the host repository

Open 💬 1 comment Opened Jul 16, 2026 by Temmie-Tem

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:

  • .git
  • tests
  • docs
  • workspace/private
  • AGENTS.md
  • GOAL.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.

  1. Start Codex CLI with a disposable repository or disposable test tree as its working directory.
  2. Delegate a nominally read-only adversarial task to a sub-agent.
  3. In a private user/mount namespace, create a Python TemporaryDirectory.
  4. Create a child directory below it and bind-mount a disposable repository or

test tree onto that child directory.

  1. Do not unmount the bind in a guaranteed finally block.
  2. Raise an exception inside the TemporaryDirectory context.
  3. 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/mountinfo before 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 under danger-full-access and approval policy
never

Additional information

Related issues — not exact duplicates

namespace confusion followed by destructive deletion advice; different
mechanism

cleanup leak; same broad mount/cleanup family, but no recursive repository
deletion

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.

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗