// Namespace-local evidence and cleanup. The JSON observation precedes teardown. import {readFileSync,writeFileSync,readdirSync,openSync,closeSync} from 'node:fs'; import {spawnSync} from 'node:child_process'; const [script,dir,mode]=process.argv.slice(2),records=dir+'/pids',worker=dir+'/worker.mjs'; const source=readFileSync(script,'utf8'); let body=source.slice(source.indexOf('# row253-supervisor-begin'),source.indexOf('# row253-supervisor-end')); // Adversarial kernel boundary controls keep the real cleanup loop and budget. if(mode==='unreadable')body=body.replace('def fields(pid):','def fields(pid):\n raise PermissionError("injected unreadable stat")'); if(mode==='unkillable')body=body.replace('signal.pidfd_send_signal(fd, signal.SIGKILL)','None # injected successful kill with a surviving member'); if(mode==='fork')writeFileSync(worker,` import {spawn} from 'node:child_process'; import {appendFileSync,readFileSync} from 'node:fs'; const record=${JSON.stringify(records)}; const sid=p=>Number(readFileSync('/proc/'+p+'/stat','utf8').split(') ').at(-1).split(' ')[3]); appendFileSync(record,JSON.stringify([process.pid,sid(process.pid)])+'\\n'); setInterval(()=>{ const child=spawn('/usr/bin/sleep',['30'],{stdio:'ignore'}); appendFileSync(record,JSON.stringify([child.pid,sid(child.pid)])+'\\n'); },4); `); else writeFileSync(worker,` import {spawn} from 'node:child_process'; import {readFileSync,writeFileSync} from 'node:fs'; const record=${JSON.stringify(records)}; const child=spawn('/bin/bash',['-c',${JSON.stringify(mode==='escape'?'exec /usr/bin/setsid /usr/bin/sleep 30':'/usr/bin/sleep 30 & echo $! > "$1"')},'--',record+'.child'],{stdio:'ignore'}); setTimeout(()=>{ const pid=${mode==='escape'?'child.pid':"Number(readFileSync(record+'.child','utf8').trim())"}; const sid=p=>Number(readFileSync('/proc/'+p+'/stat','utf8').split(') ').at(-1).split(' ')[3]); writeFileSync(record,JSON.stringify([[process.pid,sid(process.pid)],[pid,sid(pid)]])); writeFileSync(record+'.anchor',String(process.ppid)); writeFileSync(record+'.parent',readFileSync('/proc/'+pid+'/stat','utf8').split(') ').at(-1).split(' ')[1]); if(${JSON.stringify(mode)} === "anchor-death")process.kill(process.ppid,"SIGKILL"); },100); setInterval(()=>{},1000); `); const out=openSync(dir+'/out','w'),err=openSync(dir+'/err','w'); const result=spawnSync('/bin/bash',['-c',body+'\nexec /bin/bash -c "$TESTER_SUPERVISOR" -- 1 /usr/bin/setsid /usr/bin/sleep "$@"','--','/bin/bash','-c','exec "$@" >/dev/null 2>&1','--',process.execPath,worker],{encoding:'utf8',stdio:['ignore',out,err],timeout:8000,killSignal:'SIGKILL'}); closeSync(out);closeSync(err); result.stdout=readFileSync(dir+'/out','utf8');result.stderr=readFileSync(dir+'/err','utf8'); let pairs=[]; try{const text=readFileSync(records,'utf8');pairs=mode==='fork'?text.trim().split('\n').map(JSON.parse):JSON.parse(text);}catch{} const live=[]; for(const [pid] of pairs){try{const state=readFileSync('/proc/'+pid+'/stat','utf8').split(') ').at(-1).split(' ')[0];if(!['Z','X'].includes(state))live.push(pid);}catch{}} // Inventory the entire private namespace too: a fork between spawn and its // record write must not escape the assertion just because it was unrecorded. const sessionLive=[]; for(const pid of readdirSync('/proc').filter(p=>/^[0-9]+$/.test(p))){ try{ const words=readFileSync('/proc/'+pid+'/stat','utf8').split(') ').at(-1).split(' '); if(Number(words[3])===pairs[0]?.[1]&&!['Z','X'].includes(words[0]))sessionLive.push(Number(pid)); }catch{} } let parent,anchor; try{anchor=Number(readFileSync(records+".anchor","utf8"));}catch{} try{parent=Number(readFileSync(records+'.parent','utf8'));}catch{} process.stdout.write(JSON.stringify({sessionLive,parent,anchor,status:result.status,signal:result.signal,error:result.error?.message,stdout:result.stdout,stderr:result.stderr,pairs,live})); // These PIDs exist only in the private namespace. Never signal them on the host. for(const [pid] of pairs)try{process.kill(pid,'SIGKILL');}catch{}