978b6eb7
test: e2e — a shell born after the exec inherits no daemon sockets
a73x 2026-08-27 13:22
Commit message
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 |