@@ -604,6 +604,50 @@ def launch(argv, *args, **kwargs):
604604 return popen (argv , * args , ** kwargs )
605605 return patch .object (subprocess , 'Popen' , launch )
606606
607+ @unittest .skipUnless (os .name == 'posix' , 'FreeBSD kernel process observation' )
608+ def test_ps_kernel_pid_zero_preserves_owned_leader_and_live_descendant (self ):
609+ data = '0 0 0 DLs\n 2255 2178 2255 Z\n 2258 2255 2255 S\n '
610+ with patch .object (sys , 'platform' , 'freebsd14' ), self .ps_output (data ):
611+ members = commands ._process_snapshot (time .monotonic () + 2 )
612+ self .assertEqual ({0 : (0 , 0 , 'D' ), 2255 : (2178 , 2255 , 'Z' ),
613+ 2258 : (2255 , 2255 , 'S' )}, members )
614+
615+ @unittest .skipUnless (os .name == 'posix' , 'FreeBSD kernel process validation' )
616+ def test_ps_kernel_pid_zero_keeps_snapshot_validation_fail_closed (self ):
617+ for data in ('' , '0 0 0 DLs\n 0 0 0 DLs' , '0 1 0 DLs' , '0 0 1 DLs' ,
618+ '-1 0 0 DLs' , '0 -1 0 DLs' , '0 0 -1 DLs' ,
619+ '0 0 0 !' , '0 0 0 DQ' , '0 0 DLs' , '0 0 0 DLs command' ):
620+ with self .subTest (data = data ):
621+ with patch .object (sys , 'platform' , 'freebsd14' ), self .ps_output (data ):
622+ with self .assertRaisesRegex (RuntimeError , 'snapshot' ):
623+ commands ._process_snapshot (time .monotonic () + 2 )
624+
625+ @unittest .skipUnless (os .name == 'posix' , 'FreeBSD kernel row during real group cleanup' )
626+ def test_ps_kernel_pid_zero_does_not_prevent_owned_descendant_retirement (self ):
627+ popen , killpg = subprocess .Popen , os .killpg
628+ groups = []
629+ def launch (argv , * args , ** kwargs ):
630+ if argv [0 ] in ('/bin/ps' , '/usr/bin/ps' ):
631+ code = ("import subprocess,sys; "
632+ f"data=subprocess.check_output({ argv !r} ); "
633+ "kernel=any(line.split()[0] == b'0' for line in data.splitlines()); "
634+ "sys.stdout.buffer.write((b'' if kernel else b'0 0 0 DLs\\ n') + data)" )
635+ argv = self .command (code )
636+ return popen (argv , * args , ** kwargs )
637+ def signal_group (group , sig ):
638+ self .assertGreater (group , 0 , 'kernel PID/group zero must never be signalled' )
639+ groups .append (group )
640+ return killpg (group , sig )
641+ with patch .object (sys , 'platform' , 'freebsd14' ), patch .object (os , 'waitid' , None , create = True ), \
642+ patch .object (subprocess , 'Popen' , launch ), patch .object (os , 'killpg' , signal_group ):
643+ self .assert_success_retires_socket_owner ()
644+ self .assertTrue (groups , 'real owned group must be retired' )
645+ record = json .loads ((self .root / 'commands.json' ).read_text ())[- 1 ]
646+ data = Path (record ['log' ]).read_bytes ()
647+ self .assertEqual (0 , record ['exit' ])
648+ self .assertEqual (hashlib .sha256 (data ).hexdigest (), record ['sha256' ])
649+ self .assertNotIn ('error' , record )
650+
607651 @unittest .skipUnless (os .name == 'posix' , 'Darwin unknown scheduling state' )
608652 def test_ps_unknown_state_preserves_complete_numeric_membership (self ):
609653 # Captured native rows: the unknown process belongs to a different group.
0 commit comments