@@ -6515,18 +6515,43 @@ 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 ! (
6550+ "skipping: SELinux is enforcing — cross-label /proc/<pid>/exe readlink fails"
6551+ ) ;
6552+ return ;
6553+ }
6554+
65306555 let listener = TcpListener :: bind ( "127.0.0.1:0" ) . expect ( "bind listener" ) ;
65316556 let listener_port = listener. local_addr ( ) . unwrap ( ) . port ( ) ;
65326557 let stream = TcpStream :: connect ( ( "127.0.0.1" , listener_port) ) . expect ( "connect" ) ;
@@ -6567,7 +6592,10 @@ network_policies:
65676592 }
65686593 }
65696594
6570- let deadline = Instant :: now ( ) + Duration :: from_secs ( 2 ) ;
6595+ let _guard = ChildGuard ( child_pid) ;
6596+ let entrypoint_pid = std:: process:: id ( ) ;
6597+
6598+ let deadline = Instant :: now ( ) + Duration :: from_secs ( 5 ) ;
65716599 loop {
65726600 if let Ok ( link) = std:: fs:: read_link ( format ! ( "/proc/{child_pid}/exe" ) )
65736601 && link. to_string_lossy ( ) . contains ( "sleep" )
@@ -6576,38 +6604,27 @@ network_policies:
65766604 }
65776605 assert ! (
65786606 Instant :: now( ) < deadline,
6579- "child pid {child_pid} did not exec into sleep within 2s "
6607+ "child pid {child_pid} did not exec into sleep within 5s "
65806608 ) ;
65816609 std:: thread:: sleep ( Duration :: from_millis ( 20 ) ) ;
65826610 }
65836611
65846612 let cache = BinaryIdentityCache :: new ( ) ;
65856613
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) ;
6614+ let mut result = resolve_process_identity ( entrypoint_pid, peer_port, & cache) ;
65916615 for _ in 0 ..5 {
65926616 match & result {
65936617 Err ( err)
65946618 if err. reason . contains ( "No such file or directory" )
65956619 || err. reason . contains ( "os error 2" ) =>
65966620 {
65976621 std:: thread:: sleep ( Duration :: from_millis ( 50 ) ) ;
6598- result = resolve_process_identity ( std :: process :: id ( ) , peer_port, & cache) ;
6622+ result = resolve_process_identity ( entrypoint_pid , peer_port, & cache) ;
65996623 }
66006624 _ => break ,
66016625 }
66026626 }
66036627
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-
66116628 match result {
66126629 Ok ( identity) => panic ! (
66136630 "resolve_process_identity unexpectedly succeeded for shared socket owned by PID {}" ,
@@ -6620,7 +6637,7 @@ network_policies:
66206637 err. reason
66216638 ) ;
66226639 assert ! (
6623- err. reason. contains( & std :: process :: id ( ) . to_string( ) ) ,
6640+ err. reason. contains( & entrypoint_pid . to_string( ) ) ,
66246641 "error should include parent PID; got: {}" ,
66256642 err. reason
66266643 ) ;
0 commit comments