a73x

978b6eb7

test: e2e — a shell born after the exec inherits no daemon sockets

a73x   2026-08-27 13:22

Commit message
test: e2e — a shell born after the exec inherits no daemon sockets

test/e2e_14_upgrade.sh
Old New
@@ -474,6 +474,35 @@ UPFP2=$(grep -aoF -- "$UPFP" "$OUT.uag" | wc -l)
474 echo " before the exec and $UPFP2 after — no NEW answer crossed"; exit 1; } 474 echo " before the exec and $UPFP2 after — no NEW answer crossed"; exit 1; }
475 pipe_detach "agent-upgrade client" 475 pipe_detach "agent-upgrade client"
476 476
477 # A shell born of the UPGRADED daemon: a fresh named session, so its shell
478 # was forked by the exec'd image. Its fd table is the witness — asked of
479 # /proc, because a daemon cannot see its own leak. Before the fix every
480 # post-exec shell held the daemon's listener and agent sockets.
481 pipe_mux "$OUT.uagn" "$OUT.uagn.err" timeout 60 "$MUX" --sock "$SOCK71" --session post
482 pipe_send 'echo newsh=$$\n'
483 wait_grid "$SOCK71" "newsh=[0-9]" "agent-upgrade: the post-exec session never printed its pid" post
484 UPNEWSH=$(dump_session "$SOCK71" post | sed -n 's/.*newsh=\([0-9][0-9]*\).*/\1/p' | head -1)
485 [ -n "$UPNEWSH" ] || {
486 echo "e2e FAIL: agent-upgrade: no post-exec shell pid read off the grid"; dump_session "$SOCK71" post; exit 1; }
487 # `/dev/ptmx` is a class of its own: a shell's OWN terminal is the SLAVE
488 # (`/dev/pts/N`, dup2'd onto 0/1/2), so a master in a shell's fd table can
489 # only be an adopted session's, inherited. `|| true` because grep -c prints
490 # 0 AND exits 1 when nothing matches, and this suite runs under set -e.
491 UPLEAK=$(ls -l "/proc/$UPNEWSH/fd" 2>/dev/null | grep -c -E 'socket:|memfd:|/dev/ptmx' || true)
492 [ "$UPLEAK" -eq 0 ] || {
493 echo "e2e FAIL: agent-upgrade: the post-exec shell holds $UPLEAK daemon fd(s) — listeners, pty"
494 echo " masters or the manifest memfd crossed the exec without FD_CLOEXEC and were inherited:"
495 ls -l "/proc/$UPNEWSH/fd"; exit 1; }
496 pipe_detach "post-exec session client"
497
498 # The other half of the same claim, asked of the daemon's own fd table: the
499 # manifest memfd is adoption scaffolding, so once adoption is final nothing
500 # should still hold it open — a survivor accumulates one per upgrade.
501 UPDMEM=$(ls -l "/proc/$(real_pid "$D71PID")/fd" 2>/dev/null | grep -c 'memfd:mux-upgrade' || true)
502 [ "$UPDMEM" -eq 0 ] || {
503 echo "e2e FAIL: agent-upgrade: the daemon still holds $UPDMEM manifest memfd(s) after the exec"
504 ls -l "/proc/$(real_pid "$D71PID")/fd"; exit 1; }
505
477 assert_stopped "$SOCK71" "$D71PID" "agent-upgrade" "$OUT.uagstop" 506 assert_stopped "$SOCK71" "$D71PID" "agent-upgrade" "$OUT.uagstop"
478 D71PID="" 507 D71PID=""
479 softkill "$UPAGPID" || true 508 softkill "$UPAGPID" || true