From 537ce2dd4aa82f1450fac67297641f5d306de193 Mon Sep 17 00:00:00 2001 From: Jason Woltje Date: Thu, 22 Oct 2026 15:01:49 -0500 Subject: [PATCH] fix(case): silence vanished stat opens per mos-1337 N5 --- docs/plans/DEFERRED.md | 5 +++++ packages/cli/tests/case1-accept.test.mjs | 16 +++++++++++++++- scripts/case1-accept.sh | 2 +- scripts/case2-unit.sh | 2 +- 4 files changed, 22 insertions(+), 3 deletions(-) diff --git a/docs/plans/DEFERRED.md b/docs/plans/DEFERRED.md index c0e2519f1..134380c48 100644 --- a/docs/plans/DEFERRED.md +++ b/docs/plans/DEFERRED.md @@ -616,3 +616,8 @@ Effective when row276 / #1781 lands after Duckworth review and Sage's gate: all Row253 N1 closure: row276's M10 checks case1's root-owned/non-writable tool fence through both capture and legacy paths; M11 checks case1's root-side capture byte limit. Each must be killed in the accepted gate. Row253 N2 closure: both case-script headers explicitly state that a uid-changing member which cannot be killed causes inner-cleanup refusal, never successful observation. The hostile same-uid setsid-away or killed/stalled-supervisor residual remains as row253 R3. This closes none of that boundary. Instant command exit before SID confirmation remains the fail-closed residual tracked as Vikunja532 (mos-1297); row276 does not change the pinned payload. + + +## Row276 mos-1337 A5: Case1 cmdline redirection ordering + +Open follow-up: scripts/case1-accept.sh:413 (at candidate3c4c463da) uses `tr < "$ROOT/proc/$c/cmdline" 2>/dev/null` outside the supervisor. A vanished entry can print an open diagnostic before stderr is redirected. mos-1337 explicitly excludes this path from row276; it remains unchanged. The row276 supervisor repair closes only row253 N5 inside stat_fields. diff --git a/packages/cli/tests/case1-accept.test.mjs b/packages/cli/tests/case1-accept.test.mjs index 09fe00058..e5b366d9a 100644 --- a/packages/cli/tests/case1-accept.test.mjs +++ b/packages/cli/tests/case1-accept.test.mjs @@ -729,7 +729,7 @@ test("row 195 accept: every in-instance call goes through sudo -u mosaic -H env // mos-1326: assert every argv element, not flattened/substr command detection. const source=read(SCRIPT); const supervisor=source.split("<<'MOSAIC_TESTER_SUPERVISOR'")[1].split('\n').slice(1).join('\n').split('\nMOSAIC_TESTER_SUPERVISOR')[0]+'\n'; - assert.equal(createHash('sha256').update(supervisor).digest('hex'),'17b881513c35107e26f78c3722bc4a2927d206b197597be0bdbb2c8a8588bb4c'); + assert.equal(createHash('sha256').update(supervisor).digest('hex'),'34063098d7edb9067c11456165dc7535bc0e779d053c43599884d6b614528639'); const runner=/LEGACY_COMMAND_RUNNER='([\s\S]*?)'\nlegacy_display_rc/.exec(source)[1]; const f7=/F7_JS='([\s\S]*?)';\ncheck_f7/.exec(source)[1]; const raw=read(join(h.hc,'sudo-argv.jsonl')).trim().split('\n').map(line=>JSON.parse(line)); @@ -861,3 +861,17 @@ test('row276 full acceptance keeps all five healthy calls in both sudo models',a assert.deepEqual(await pollFixtureGone(files),[],mode); } }); + +// mos-1337 / row253 N5: the read must silence failure opening a vanished PID. +test('row276 vanished pid stat read returns 3 with empty stderr in both scripts',()=>{ + const missingPid='2147483647'; + assert.equal(existsSync(`/proc/${missingPid}`),false); + for(const script of [SCRIPT,join(REPO,'scripts/case2-unit.sh')]){ + const fields=/^stat_fields\(\) \{[\s\S]*?^\}/m.exec(read(script)); + assert.ok(fields,'stat_fields exists'); + const r=spawnSync('/bin/bash',['-c',fields[0]+'\nstat_fields "$1"','--',missingPid],{encoding:'utf8'}); + assert.equal(r.status,3,script); + assert.equal(r.stdout,'',script); + assert.equal(r.stderr,'',script); + } +}); diff --git a/scripts/case1-accept.sh b/scripts/case1-accept.sh index 5ab553b91..6163a6205 100755 --- a/scripts/case1-accept.sh +++ b/scripts/case1-accept.sh @@ -124,7 +124,7 @@ child="" timer="" verified=0 stat_fields() { local pid="$1" raw="" rest # EOF is expected: stat has no NUL. Decide on content, not read's status. - IFS= read -r -d '' raw < "/proc/$pid/stat" 2>/dev/null || : + IFS= read -r -d '' raw 2>/dev/null < "/proc/$pid/stat" || : [ -n "$raw" ] || { [ -e "/proc/$pid/stat" ] && return 1; return 3; } rest="${raw##*) }" [ "$rest" != "$raw" ] || return 1 diff --git a/scripts/case2-unit.sh b/scripts/case2-unit.sh index 2a471a9ab..2479f55d5 100755 --- a/scripts/case2-unit.sh +++ b/scripts/case2-unit.sh @@ -163,7 +163,7 @@ child="" timer="" verified=0 stat_fields() { local pid="$1" raw="" rest # EOF is expected: stat has no NUL. Decide on content, not read's status. - IFS= read -r -d '' raw < "/proc/$pid/stat" 2>/dev/null || : + IFS= read -r -d '' raw 2>/dev/null < "/proc/$pid/stat" || : [ -n "$raw" ] || { [ -e "/proc/$pid/stat" ] && return 1; return 3; } rest="${raw##*) }" [ "$rest" != "$raw" ] || return 1