Skip to content

Batch: seven sandbox, compiler and CI hardening PRs from @adilalperenciftci, with follow-ups - #3456

Merged
paul-hammant merged 9 commits into
mainfrom
batch/contrib-2026-10-29
Oct 29, 2026
Merged

paul-hammant merged 9 commits into
mainfrom
batch/contrib-2026-10-29

Conversation

@paul-hammant

@paul-hammant paul-hammant commented Oct 29, 2026 •

Copy link
Copy Markdown
Collaborator

Credit: the work in this batch is @adilalperenciftci's: seven well-tested hardening fixes across the compiler's capability gates, the sandbox runtime and CI, each closing a real gap. Thank you. Two small follow-ups of ours sit on top; the commits carry his authorship.

Seven PRs by @adilalperenciftci, reviewed and cherry-picked as one batch with the author kept on each commit, plus two follow-up commits of ours. #3441 is held out: it conflicts with #3334's per-thread grant levels (#2955), see the comment there.

PR Change Review
#3428 --emit=lib gates module families by name or name. prefix; adds std.file, std.dir, std.udp, std.dl, contrib.host, and std.http.* / std.os.* children Closes real bypasses; only more is rejected
#3444 effect tags check every clause of a tagged function and of its callees A later clause could call os.system under @no_os
#3431 hide / seal checked before the DSL receiver fallback, and applied inside trailing closures Both were bypassable; no other .ae in the repo uses them
#3434 the FreeBSD VM job runs the Capsicum portability spec and tests/freebsd/run.sh Two lines in the existing job; no new actions, secrets or permissions
#3433 each sandboxed spawn gets its own shared-memory object (O_EXCL, 0600, retried) The fixed per-pid name was shared between concurrent launches
#3436 a configured but unreadable or oversized grant source denies everything Used to run unrestricted
#3440 execve with an explicit environment keeps the sandbox controls Merged with our hardening below

Our follow-ups:

  • f92a672: runtime: preserve sandbox controls across explicit exec envs #3440 copied the controls from live environ, which the contained process can edit (setenv a grant file of its own, unsetenv the shm, then execve). They are now snapshotted once in the constructor into fixed buffers, and execve rebuilds the child's controls from the snapshot (no malloc; an overflowing snapshot denies). New tamper case in sandbox_exec_env_inherit; it fails on 0f58cf8.
  • e18798f: the grant serialiser in spawn_sandboxed_{linux,bsd}.c advanced pos by snprintf's would-be length, so an entry starting under the 8000-byte cut-off could take pos past the 8192-byte buffer, and the shm write then read past it. It now denies the spawn. ATOMIC_VAR_INIT (deprecated since C17) dropped. Two lines in docs/containment-sandbox.md (fail-closed grant sources; only execve is interposed, not execvp / posix_spawn). New test sandbox_grant_overflow (an entry crossing 8192); it fails with the old serialiser.

Verification:

  • macOS (M6): make test 633 passed; emit_lib_banned 12/12, emit_lib_with_capability 35/35, hide_seal_directives 19/19, effect_tags pass. The sandbox tests skip on macOS.
  • Linux x86_64 (Ubuntu 22.04, gcc): runtime C suite 634/634; Aether tests 1977 passed, the one failure being an earlier version of our overflow test, since fixed. All sandbox tests pass with gcc and clang, and both of our new tests fail against the code before their fix. A full make ci on the final tree is still running. I'll add its result here.
  • FreeBSD: not run locally (the lab box is down); spawn_sandboxed_bsd.c's hunk is identical to the Linux one, and ci: run FreeBSD containment tests #3434 makes this PR's CI run the FreeBSD tests.

Follow-ups noted, not in this PR: the trust anchor's own pathLenConstraint (#3432); still-ungated capability-bearing modules under --emit=lib (std.zip/std.tar extract, std.ipc, contrib.sqlite, std.sandbox); effect classification doesn't know udp, dl or import aliases; a grant file the contained process can write remains writable (prefer the shm source).

When this merges I'll close #3428, #3431, #3433, #3434, #3436, #3440 and #3444 pointing here.

🤖 Generated with Claude Code

adilalperenciftci and others added 9 commits October 29, 2026 08:50
Signed-off-by: Adil Alperen Çiftci <adilalperenciftci@users.noreply.github.com>
Signed-off-by: Adil Alperen Çiftci <adilalperenciftci@users.noreply.github.com>
Signed-off-by: Adil Alperen Çiftci <adilalperenciftci@users.noreply.github.com>
Signed-off-by: Adil Alperen Çiftci <adilalperenciftci@users.noreply.github.com>
Signed-off-by: Adil Alperen Çiftci <adilalperenciftci@users.noreply.github.com>
Signed-off-by: Adil Alperen Çiftci <adilalperenciftci@users.noreply.github.com>
Signed-off-by: Adil Alperen Çiftci <adilalperenciftci@users.noreply.github.com>
The execve wrapper rebuilt the child's sandbox controls from live environ,
which the contained process can edit (setenv a grant file of its own,
unsetenv the shm, then execve). Snapshot LD_PRELOAD, AETHER_SANDBOX_* and
AETHER_CAPSICUM once in sandbox_init into static bounded buffers and merge
from the snapshot; if they do not fit, execve fails closed with EPERM.
Adds a tamper case to sandbox_exec_env_inherit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
pos += snprintf(...) could pass buf[8192] (snprintf returns the would-be
length), so the following (pos+1)-byte write read past the stack buffer.
Fail the spawn (-1) when an entry does not fit, on Linux and BSD. Adds
sandbox_grant_overflow. ATOMIC_VAR_INIT is deprecated since C17.

docs: note #3436 and #3440 behaviour in containment-sandbox.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@paul-hammant

Copy link
Copy Markdown
Collaborator Author

Full make ci on the final tree (e18798f), real x86_64 Ubuntu 22.04, gcc, HARDEN=0: exit 0, every stage. Runtime C suite 634 passed; Aether tests 1978 passed, 0 failed, 23 skipped.

🤖 Generated with Claude Code

@adilalperenciftci

Copy link
Copy Markdown
Contributor

The only red job is macOS / Clang (x86_64) [1/2]: integration_sweep_timeout timed out waiting for the Perl-bound SIGTERM (rc=124). The other 976 tests in that shard and every other matrix job passed. I do not have permission to rerun the workflow; could you rerun the failed job?

@paul-hammant
paul-hammant merged commit 9364d6b into main Oct 29, 2026
71 of 72 checks passed
@paul-hammant
paul-hammant deleted the batch/contrib-2026-10-29 branch October 29, 2026 11:42
paul-hammant pushed a commit to paul-hammant/aether-fork that referenced this pull request Oct 30, 2026
Signed-off-by: Adil Alperen Çiftci <adilalperenciftci@users.noreply.github.com>

Rebased onto main after aether-lang-dev#3456; conflicts with e18798f resolved in favour of aether_sandbox_grants.h.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants