@@ -6515,18 +6515,41 @@ network_policies:
65156515
65166516 #[ cfg( target_os = "linux" ) ]
65176517 #[ test]
6518+ // TODO: exec'ing /bin/sleep (SELinux label bin_t) from a user_home_t test
6519+ // binary causes /proc/<pid>/exe readlink to return ENOENT on
6520+ // SELinux-enforcing hosts. Fix by building a test-sleep-helper binary in
6521+ // the same crate so it inherits the user_home_t label.
65186522 fn resolve_process_identity_denies_fork_exec_shared_socket_ambiguity ( ) {
65196523 use crate :: identity:: BinaryIdentityCache ;
65206524 use std:: ffi:: CString ;
65216525 use std:: net:: { TcpListener , TcpStream } ;
65226526 use std:: os:: fd:: AsRawFd ;
65236527 use std:: time:: { Duration , Instant } ;
65246528
6529+ struct ChildGuard ( libc:: pid_t ) ;
6530+ impl Drop for ChildGuard {
6531+ fn drop ( & mut self ) {
6532+ #[ allow( unsafe_code) ]
6533+ unsafe {
6534+ libc:: kill ( self . 0 , libc:: SIGKILL ) ;
6535+ libc:: waitpid ( self . 0 , std:: ptr:: null_mut ( ) , 0 ) ;
6536+ }
6537+ }
6538+ }
6539+
65256540 if !std:: path:: Path :: new ( "/bin/sleep" ) . exists ( ) {
65266541 eprintln ! ( "skipping: /bin/sleep not available" ) ;
65276542 return ;
65286543 }
65296544
6545+ if std:: process:: Command :: new ( "getenforce" )
6546+ . output ( )
6547+ . is_ok_and ( |o| String :: from_utf8_lossy ( & o. stdout ) . trim ( ) == "Enforcing" )
6548+ {
6549+ eprintln ! ( "skipping: SELinux is enforcing — cross-label /proc/<pid>/exe readlink fails" ) ;
6550+ return ;
6551+ }
6552+
65306553 let listener = TcpListener :: bind ( "127.0.0.1:0" ) . expect ( "bind listener" ) ;
65316554 let listener_port = listener. local_addr ( ) . unwrap ( ) . port ( ) ;
65326555 let stream = TcpStream :: connect ( ( "127.0.0.1" , listener_port) ) . expect ( "connect" ) ;
@@ -6567,7 +6590,10 @@ network_policies:
65676590 }
65686591 }
65696592
6570- let deadline = Instant :: now ( ) + Duration :: from_secs ( 2 ) ;
6593+ let _guard = ChildGuard ( child_pid) ;
6594+ let entrypoint_pid = std:: process:: id ( ) ;
6595+
6596+ let deadline = Instant :: now ( ) + Duration :: from_secs ( 5 ) ;
65716597 loop {
65726598 if let Ok ( link) = std:: fs:: read_link ( format ! ( "/proc/{child_pid}/exe" ) )
65736599 && link. to_string_lossy ( ) . contains ( "sleep" )
@@ -6576,38 +6602,27 @@ network_policies:
65766602 }
65776603 assert ! (
65786604 Instant :: now( ) < deadline,
6579- "child pid {child_pid} did not exec into sleep within 2s "
6605+ "child pid {child_pid} did not exec into sleep within 5s "
65806606 ) ;
65816607 std:: thread:: sleep ( Duration :: from_millis ( 20 ) ) ;
65826608 }
65836609
65846610 let cache = BinaryIdentityCache :: new ( ) ;
65856611
6586- // Resolve with a brief retry loop — under heavy CI load the child's
6587- // procfs entry can momentarily fail to resolve even though the loop
6588- // above just verified `/proc/<pid>/exe` pointed at `sleep`. Retry a
6589- // few times before declaring failure so the test is not flaky.
6590- let mut result = resolve_process_identity ( std:: process:: id ( ) , peer_port, & cache) ;
6612+ let mut result = resolve_process_identity ( entrypoint_pid, peer_port, & cache) ;
65916613 for _ in 0 ..5 {
65926614 match & result {
65936615 Err ( err)
65946616 if err. reason . contains ( "No such file or directory" )
65956617 || err. reason . contains ( "os error 2" ) =>
65966618 {
65976619 std:: thread:: sleep ( Duration :: from_millis ( 50 ) ) ;
6598- result = resolve_process_identity ( std :: process :: id ( ) , peer_port, & cache) ;
6620+ result = resolve_process_identity ( entrypoint_pid , peer_port, & cache) ;
65996621 }
66006622 _ => break ,
66016623 }
66026624 }
66036625
6604- // libc/syscall FFI requires unsafe
6605- #[ allow( unsafe_code) ]
6606- unsafe {
6607- libc:: kill ( child_pid, libc:: SIGKILL ) ;
6608- libc:: waitpid ( child_pid, std:: ptr:: null_mut ( ) , 0 ) ;
6609- }
6610-
66116626 match result {
66126627 Ok ( identity) => panic ! (
66136628 "resolve_process_identity unexpectedly succeeded for shared socket owned by PID {}" ,
@@ -6620,7 +6635,7 @@ network_policies:
66206635 err. reason
66216636 ) ;
66226637 assert ! (
6623- err. reason. contains( & std :: process :: id ( ) . to_string( ) ) ,
6638+ err. reason. contains( & entrypoint_pid . to_string( ) ) ,
66246639 "error should include parent PID; got: {}" ,
66256640 err. reason
66266641 ) ;
0 commit comments