Rollup merge of #126827 - the8472:pidfd-spawn, r=workingjubilee · model-checking/verify-rust-std@f66bd5f (original) (raw)
`@@ -449,17 +449,82 @@ impl Command {
`
449
449
`use crate::mem::MaybeUninit;
`
450
450
`use crate::sys::weak::weak;
`
451
451
`use crate::sys::{self, cvt_nz, on_broken_pipe_flag_used};
`
``
452
`+
#[cfg(target_os = "linux")]
`
``
453
`+
use core::sync::atomic::{AtomicU8, Ordering};
`
452
454
``
453
455
`if self.get_gid().is_some()
`
454
456
` || self.get_uid().is_some()
`
455
457
` || (self.env_saw_path() && !self.program_is_path())
`
456
458
` || !self.get_closures().is_empty()
`
457
459
` || self.get_groups().is_some()
`
458
``
`-
|| self.get_create_pidfd()
`
459
460
`{
`
460
461
`return Ok(None);
`
461
462
`}
`
462
463
``
``
464
`+
cfg_if::cfg_if! {
`
``
465
`+
if #[cfg(target_os = "linux")] {
`
``
466
`+
weak! {
`
``
467
`+
fn pidfd_spawnp(
`
``
468
`+
*mut libc::c_int,
`
``
469
`+
*const libc::c_char,
`
``
470
`+
*const libc::posix_spawn_file_actions_t,
`
``
471
`+
*const libc::posix_spawnattr_t,
`
``
472
`+
*const *mut libc::c_char,
`
``
473
`+
*const *mut libc::c_char
`
``
474
`+
) -> libc::c_int
`
``
475
`+
}
`
``
476
+
``
477
`+
weak! { fn pidfd_getpid(libc::c_int) -> libc::c_int }
`
``
478
+
``
479
`+
static PIDFD_SUPPORTED: AtomicU8 = AtomicU8::new(0);
`
``
480
`+
const UNKNOWN: u8 = 0;
`
``
481
`+
const SPAWN: u8 = 1;
`
``
482
`+
// Obtaining a pidfd via the fork+exec path might work
`
``
483
`+
const FORK_EXEC: u8 = 2;
`
``
484
`+
// Neither pidfd_spawn nor fork/exec will get us a pidfd.
`
``
485
`+
// Instead we'll just posix_spawn if the other preconditions are met.
`
``
486
`+
const NO: u8 = 3;
`
``
487
+
``
488
`+
if self.get_create_pidfd() {
`
``
489
`+
let mut support = PIDFD_SUPPORTED.load(Ordering::Relaxed);
`
``
490
`+
if support == FORK_EXEC {
`
``
491
`+
return Ok(None);
`
``
492
`+
}
`
``
493
`+
if support == UNKNOWN {
`
``
494
`+
support = NO;
`
``
495
`+
let our_pid = crate::process::id();
`
``
496
`+
let pidfd = cvt(unsafe { libc::syscall(libc::SYS_pidfd_open, our_pid, 0) } as c_int);
`
``
497
`+
match pidfd {
`
``
498
`+
Ok(pidfd) => {
`
``
499
`+
support = FORK_EXEC;
`
``
500
`+
if let Some(Ok(pid)) = pidfd_getpid.get().map(|f| cvt(unsafe { f(pidfd) } as i32)) {
`
``
501
`+
if pidfd_spawnp.get().is_some() && pid as u32 == our_pid {
`
``
502
`+
support = SPAWN
`
``
503
`+
}
`
``
504
`+
}
`
``
505
`+
unsafe { libc::close(pidfd) };
`
``
506
`+
}
`
``
507
`+
Err(e) if e.raw_os_error() == Some(libc::EMFILE) => {
`
``
508
`+
// We're temporarily(?) out of file descriptors. In this case obtaining a pidfd would also fail
`
``
509
`+
// Don't update the support flag so we can probe again later.
`
``
510
`+
return Err(e)
`
``
511
`+
}
`
``
512
`+
_ => {}
`
``
513
`+
}
`
``
514
`+
PIDFD_SUPPORTED.store(support, Ordering::Relaxed);
`
``
515
`+
if support == FORK_EXEC {
`
``
516
`+
return Ok(None);
`
``
517
`+
}
`
``
518
`+
}
`
``
519
`+
core::assert_matches::debug_assert_matches!(support, SPAWN | NO);
`
``
520
`+
}
`
``
521
`+
} else {
`
``
522
`+
if self.get_create_pidfd() {
`
``
523
`+
unreachable!("only implemented on linux")
`
``
524
`+
}
`
``
525
`+
}
`
``
526
`+
}
`
``
527
+
463
528
`// Only glibc 2.24+ posix_spawn() supports returning ENOENT directly.
`
464
529
`#[cfg(all(target_os = "linux", target_env = "gnu"))]
`
465
530
`{
`
`@@ -543,9 +608,6 @@ impl Command {
`
543
608
``
544
609
`let pgroup = self.get_pgroup();
`
545
610
``
546
``
`-
// Safety: -1 indicates we don't have a pidfd.
`
547
``
`-
let mut p = unsafe { Process::new(0, -1) };
`
548
``
-
549
611
`struct PosixSpawnFileActions<'a>(&'a mut MaybeUninitlibc::posix_spawn_file_actions_t);
`
550
612
``
551
613
`impl Drop for PosixSpawnFileActions<'_> {
`
`@@ -640,6 +702,47 @@ impl Command {
`
640
702
`#[cfg(target_os = "nto")]
`
641
703
`let spawn_fn = retrying_libc_posix_spawnp;
`
642
704
``
``
705
`+
#[cfg(target_os = "linux")]
`
``
706
`+
if self.get_create_pidfd() && PIDFD_SUPPORTED.load(Ordering::Relaxed) == SPAWN {
`
``
707
`+
let mut pidfd: libc::c_int = -1;
`
``
708
`+
let spawn_res = pidfd_spawnp.get().unwrap()(
`
``
709
`+
&mut pidfd,
`
``
710
`+
self.get_program_cstr().as_ptr(),
`
``
711
`+
file_actions.0.as_ptr(),
`
``
712
`+
attrs.0.as_ptr(),
`
``
713
`+
self.get_argv().as_ptr() as *const _,
`
``
714
`+
envp as *const _,
`
``
715
`+
);
`
``
716
+
``
717
`+
let spawn_res = cvt_nz(spawn_res);
`
``
718
`+
if let Err(ref e) = spawn_res
`
``
719
`+
&& e.raw_os_error() == Some(libc::ENOSYS)
`
``
720
`+
{
`
``
721
`+
PIDFD_SUPPORTED.store(FORK_EXEC, Ordering::Relaxed);
`
``
722
`+
return Ok(None);
`
``
723
`+
}
`
``
724
`+
spawn_res?;
`
``
725
+
``
726
`+
let pid = match cvt(pidfd_getpid.get().unwrap()(pidfd)) {
`
``
727
`+
Ok(pid) => pid,
`
``
728
`+
Err(e) => {
`
``
729
`+
// The child has been spawned and we are holding its pidfd.
`
``
730
`+
// But we cannot obtain its pid even though pidfd_getpid support was verified earlier.
`
``
731
`+
// This might happen if libc can't open procfs because the file descriptor limit has been reached.
`
``
732
`+
libc::close(pidfd);
`
``
733
`+
return Err(Error::new(
`
``
734
`+
e.kind(),
`
``
735
`+
"pidfd_spawnp succeeded but the child's PID could not be obtained",
`
``
736
`+
));
`
``
737
`+
}
`
``
738
`+
};
`
``
739
+
``
740
`+
return Ok(Some(Process::new(pid, pidfd)));
`
``
741
`+
}
`
``
742
+
``
743
`+
// Safety: -1 indicates we don't have a pidfd.
`
``
744
`+
let mut p = Process::new(0, -1);
`
``
745
+
643
746
`let spawn_res = spawn_fn(
`
644
747
`&mut p.pid,
`
645
748
`self.get_program_cstr().as_ptr(),
`
`@@ -786,6 +889,12 @@ pub struct Process {
`
786
889
``
787
890
`impl Process {
`
788
891
`#[cfg(target_os = "linux")]
`
``
892
`+
/// # Safety
`
``
893
`+
///
`
``
894
`` +
/// pidfd
must either be -1 (representing no file descriptor) or a valid, exclusively owned file
``
``
895
`+
/// descriptor (See [I/O Safety]).
`
``
896
`+
///
`
``
897
`+
/// [I/O Safety]: crate::io#io-safety
`
789
898
`unsafe fn new(pid: pid_t, pidfd: pid_t) -> Self {
`
790
899
`use crate::os::unix::io::FromRawFd;
`
791
900
`use crate::sys_common::FromInner;
`