Restricted Linux sandbox breaks connected Unix socket sends and asyncio wakeups

Open 💬 1 comment Opened Jul 17, 2026 by everySympathy

Summary

The restricted Linux network seccomp filter unconditionally denies sendto(2). On Linux, send(2) is implemented as sendto(..., dest_addr = NULL), so this also breaks writes to already-connected Unix-domain sockets.

One visible consequence is that Python asyncio cross-thread wakeups stop working inside the default Codex workspace sandbox. loop.call_soon_threadsafe() writes to asyncio's AF_UNIX wakeup socket; the write receives EPERM, asyncio suppresses that OSError, and the event loop remains asleep.

Reproduction

On current main (315195492c), build Codex and run:

./target/debug/codex sandbox -P :workspace -C /tmp /usr/bin/python3 -c 'import asyncio, threading; loop=asyncio.new_event_loop(); threading.Thread(target=loop.run_forever, daemon=True).start(); print(asyncio.run_coroutine_threadsafe(asyncio.sleep(0, result=42), loop).result(timeout=2))'

Expected:

42

Actual:

TimeoutError

This also causes Daft's native runner to hang on a simple collect() operation.

Isolation

  • The same program succeeds outside the Codex sandbox.
  • It succeeds with the same bubblewrap filesystem isolation and a full-network Codex profile.
  • It succeeds with plain bubblewrap plus --unshare-net.
  • It fails only when Codex's restricted network seccomp filter is installed.

Root cause and possible fix

In codex-rs/linux-sandbox/src/landlock.rs, restricted mode unconditionally denies SYS_sendto.

A narrowly scoped fix is to deny sendto only when its fifth argument (dest_addr) is non-null. This permits send() on an already-connected socket, including AF_UNIX socketpairs used for local IPC, while continuing to deny sends to explicit destinations such as unconnected Unix datagram socket paths.

I have a tested patch with regression coverage for both sides of this boundary:

  • connected AF_UNIX socket send() succeeds;
  • sendto() with an explicit destination still returns EPERM.

The codex-linux-sandbox test suite passes (120/120), and both the minimal asyncio reproducer and the original Daft collect() scenario pass with the patched binary.

View original on GitHub ↗

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