Codex Asks What Linux It Is Running On 96 Times to Say "ok"

grith team··7 min read·security
grith is live

A security proxy for AI coding agents, enforced at the OS level. Install grith and put a real boundary around your agent.

A terminal panel listing 96 real lsb_release -a spawns with their timestamps from a single Codex session, running from 0.063s to 9.702s, overlaid with a large multiplied-by-96, the command name, 'one session, 9.7 seconds' and 'claude code, same task: 4 process spawns'.
All ninety-six of them, with their real timestamps, from run 5's audit log.

Ask an AI coding agent to reply with the single word "ok" and watch what happens. Not at the prompt level - at the syscall level, where processes get spawned and sockets get opened.

We did this five times each for Claude Code and Codex, cold starts, identical trivial task, same repository, same machine. The headline finding: Codex spawns lsb_release -a roughly 96 times per session. That is a command whose job is to report which Linux distribution is running. The answer cannot change between the first call and the ninety-sixth.

The setup

Both agents ran under grith supervision, which records every process spawn, file operation, DNS query and socket event to a local audit log. That log is the instrument: rather than a staged demo, these are ordinary supervised sessions read back afterwards.

  • Task: "reply with the single word ok" - deliberately trivial, so everything measured is startup behaviour, not work.
  • Five cold runs per agent, same repository, same machine. Figures below are medians across the five runs.
  • Versions: Claude Code 2.1.250, Codex CLI 0.149.1, grith 0.3.3.

One methodology note, because it changed our results before publication: the first version of the analysis sliced the audit database by wall clock, and promptly attributed DNS traffic from a different concurrent session - including a third-party domain - to the agent under test. The harness now anchors on the session start event and follows that session id, so nothing else running on the machine can leak into the numbers. That correction is the difference between a measurement and an accusation.

One trivial session, measured

Event kindClaude CodeCodex
Process spawns4111
DNS queries1226
Socket listens0121
io_uring setup attempts09
Directories created31
Files deleted20

Claude Code's entire session is four process spawns. Codex's is 111, and about 88% of them are the same command.

Two event timelines from the grith audit log on a shared zero-to-ten-second axis. Codex 0.149.1: 111 process spawn ticks, 96 of them lsb_release -a highlighted in green, arriving in clumps across 9.7 seconds, with nine denied io_uring_setup attempts marked in red below the line. Claude Code 2.1.250: four spawns near the start, exiting at 4.9 seconds.
Run 5, every event at its real timestamp. Same task, same repo, same machine.

Finding 1: lsb_release, 96 times

Per run, Codex spawned lsb_release -a 112, 92, 104, 88 and 96 times - a median of 96. The first arrives about 63ms after session start, before Codex has run any shell; roughly half land inside the first second, and the rest arrive in clumps across the remaining seconds of the session, still going just before it exits. The count varying between runs suggests it is per-something rather than a fixed loop, but we have not identified what. It is not the operator's dotfiles: lsb_release appears nowhere in .bashrc, .bash_profile or .profile on the test machine.

To be clear about what this is and is not: it is waste, not exfiltration. lsb_release reads local release metadata and prints it. But a process tree that asks the same immutable question ninety-six times in a ten-second session is a process tree nobody has looked at from below. If you know what produces this - a runtime default, a per-worker environment probe, something in the Node ecosystem - we would genuinely like to know.

Finding 2: your login shell runs seven times

Codex spawns bash -lc seven times per session, the first around 700ms in. A login shell sources your profile, which means whatever is in your dotfiles executes as part of an ordinary agent session. On this machine that meant version-manager and package-tool hooks from the operator's own configuration - not Codex behaviour, and not secret. That is exactly the point: the mechanism runs your environment, whatever it contains. A compromised dotfile would run just the same, seven times, in a session that only ever needed to say ok.

Claude Code spawned no login shell at all in these sessions.

Finding 3: nine reaches for io_uring

Codex attempts io_uring_setup nine times per session; grith denies every one. io_uring matters here because it is a documented route around syscall-level security filtering - operations submitted through its rings do not surface as individual syscalls. We are not implying intent: this is far more likely a runtime or library default than anything deliberate. But an agent runtime reaching for the one I/O mechanism that syscall filters cannot see is worth stating plainly. In these runs, Claude Code did not attempt it.

What they talk to

Domains resolved during the session, median counts:

  • Claude Code: api.anthropic.com (8), mcp-proxy.anthropic.com (2), and a Datadog log-intake host (2)2
  • Codex: chatgpt.com (22), ab.chatgpt.com (2), registry.npmjs.org (2)

The npm registry resolution comes from Codex running npm exec for a Playwright package during the session; we have not yet confirmed what triggers it, but a package fetch during a session whose task needed no packages is worth knowing about regardless of the reason.

Honest limits

One machine, one operator's configuration, five runs per agent, two specific versions. Counts vary between runs and will vary more between machines. None of this is hidden behaviour - all of it is visible to anyone who looks at the right layer, which is rather the point: almost nobody looks. We would welcome numbers from other machines, and the harness below makes that a five-minute job.

Run it yourself

The harness is a shell script and an analysis script: start a supervised session, run the trivial task, then slice the audit log by that session's id and count events by kind. If your numbers disagree with ours, that is interesting and we want to see them.

The broader argument - that the interesting security questions about AI agents are answered below the prompt, at the boundary where processes and sockets are real - is the same one behind our earlier traces of what these agents read and what happened when one went looking for credentials. The most trivial session you can run turns out to be just as informative as the worst one.

Footnotes

  1. All twelve are binds to port 0, which indicates ephemeral port allocation rather than a durable listening service. We have not characterised these sockets further, so we are deliberately not describing this as "opening twelve listeners".

  2. We have not confirmed whether the Datadog resolution comes from Claude Code itself or from tooling configured on the test machine, so treat that row as unattributed until we do.

Like this post? Share it.