Skip to content

fix: bounded WP-uffd accept (#261) + harden tag/id path joins (#259, #260)#262

Merged
WaylandYang merged 1 commit into
mainfrom
fix/security-triage-259-260-261
Jun 24, 2026
Merged

fix: bounded WP-uffd accept (#261) + harden tag/id path joins (#259, #260)#262
WaylandYang merged 1 commit into
mainfrom
fix/security-triage-259-260-261

Conversation

@WaylandYang

Copy link
Copy Markdown
Contributor

Triage of three automated-audit reports from @asmit25805. I verified each against the actual code before acting — one real bug + two low-severity hardenings. The "🟠 High" ratings were overstated, but all three fixes are cheap and correct.

#261 — real (robustness)

request_wp_uffd runs accept() in a thread with the UnixListener moved into it, then issues a PUT (10 s timeout) that makes Firecracker dial back. If FC accepts the PUT but never connects (or the PUT fails), the blocking accept() never returns → accept_thread.join() hangs forever. The old comment ("listener drops at end of scope → accept unblocks") was wrong — the listener is owned by the thread. Fixed with a deadline-bounded accept_with_deadline() (deadline = API timeout + 2 s) so a non-connecting FC is a clean error, not a hang. Linux-only regression test.

#259 — defense in depth

pack_chain joined a chain parent_tag onto snap_root unvalidated. The value is from a local snapshot.json the packer owns (the untrusted direction — unpack — already validates via is_safe_pack_tag), but a corrupted edge like ../../etc would escape snap_root. Now rejected with the same rule.

#260 — defense in depth

delete_sandbox spliced a sandbox id into the request path unvalidated. Ids from --all/--tag come from the daemon's own list (trusted); an explicit CLI id is user-controlled and ../snapshots would traverse the URL. No privilege boundary is crossed (CLI → operator's own daemon), but reject [^A-Za-z0-9_-] for a clear error instead of a confusing 404. New unit tests.

Tests

  • accept_with_deadline times-out test (Linux), is_valid_sandbox_id + delete_sandbox-rejects-bad-id tests.
  • cargo build/clippy -D warnings/fmt --check clean; cargo test -p forkd-vmm -p forkd-cli green on the dev box.

🤖 Generated with Claude Code

…260)

Triage of three automated-audit reports from @asmit25805. Verified each
against the code — one real bug, two low-severity hardening (the "High
security" ratings were overstated, but the fixes are cheap and correct).

#261 (real, robustness): request_wp_uffd spawns an accept() thread with
the UnixListener MOVED into it, then issues a PUT (10s timeout) that makes
FC dial back. If FC accepts the PUT but never connects — or the PUT itself
fails — the blocking accept() never returns, so accept_thread.join() wedges
forever. The old "the listener drops at end of scope" comment was wrong:
the listener is owned by the thread, not the outer scope. Extracted a
deadline-bounded accept_with_deadline() (non-blocking poll, deadline =
API timeout + 2s) so a non-connecting FC surfaces as a clean error instead
of a hang. Linux-only regression test.

#259 (defense in depth): pack_chain joined a chain's parent_tag onto
snap_root without validation. The value comes from a local snapshot.json
the packer owns (the untrusted direction — unpack — already validates via
is_safe_pack_tag), but a corrupted/hand-edited edge like `../../etc` would
escape snap_root. Reject non-tag names with the same rule unpack uses.

#260 (defense in depth): delete_sandbox spliced a sandbox id into the
request path unvalidated. Ids from --all/--tag come from the daemon's own
list response (trusted); an explicit CLI id is user-controlled, and
`../snapshots` would traverse the URL to another endpoint. No privilege
boundary is crossed (the CLI talks only to the operator's own daemon), but
reject `[^A-Za-z0-9_-]` up front for a clear error instead of a confusing
404. New sdk-side unit tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@WaylandYang WaylandYang merged commit 055cc7b into main Jun 24, 2026
3 checks passed
@WaylandYang WaylandYang deleted the fix/security-triage-259-260-261 branch June 24, 2026 08:11
@asmit25805

Copy link
Copy Markdown

Thanks for the quick triage and the fixes, Wayland! Glad the bounded accept and the defense-in-depth path validation are in main now. Appreciate the fast turnaround!

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