Skip to content

Commit fc065b9

Browse files
SteveL-MSFTCopilot
andcommitted
Serialize macOS test forks to avoid libSystem initialization races
Prevent concurrent native test forks from crashing in macOS child-side libSystem initialization before exec. Keep other tests parallel and report child wait status before asserting SIGINT output. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
1 parent 439f1be commit fc065b9

1 file changed

Lines changed: 23 additions & 4 deletions

File tree

‎rust/libpsl-native/tests/native.rs‎

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ use std::path::{Path, PathBuf};
1313
use std::ptr;
1414
use std::sync::atomic::{AtomicU64, Ordering};
1515

16+
// Concurrent first forks can crash macOS libSystem's child-side initialization.
17+
#[cfg(target_os = "macos")]
18+
static FORK_LOCK: std::sync::Mutex<()> = std::sync::Mutex::new(());
19+
1620
struct Fixture(PathBuf);
1721

1822
impl Fixture {
@@ -263,6 +267,8 @@ fn error_category_mapping() {
263267
}
264268

265269
unsafe fn spawn(args: &[&CStr], cwd: Option<&CStr>, flags: i32) -> (i32, i32, i32, i32) {
270+
#[cfg(target_os = "macos")]
271+
let _fork_guard = FORK_LOCK.lock().unwrap();
266272
let mut pointers: Vec<_> = args.iter().map(|arg| arg.as_ptr()).collect();
267273
pointers.push(ptr::null());
268274
let env = [c"PSL_RUST_TEST=present".as_ptr(), ptr::null()];
@@ -336,9 +342,17 @@ fn child_only_sigint_suppression() {
336342
let (pid, input, output, error) =
337343
spawn(&[c"sh", c"-c", c"kill -INT $$; printf survived"], None, 1);
338344
libc::close(input);
339-
assert_eq!(read_fd(output), "survived");
340-
assert_eq!(read_fd(error), "");
341-
assert_eq!(WaitPid(pid, false), pid);
345+
let text = read_fd(output);
346+
let diagnostic = read_fd(error);
347+
let mut status = 0;
348+
assert_eq!(libc::waitpid(pid, &raw mut status, 0), pid);
349+
assert!(
350+
libc::WIFEXITED(status),
351+
"child terminated abnormally (wait status {status:#x}): {diagnostic}"
352+
);
353+
assert_eq!(libc::WEXITSTATUS(status), 0, "{diagnostic}");
354+
assert_eq!(text, "survived");
355+
assert_eq!(diagnostic, "");
342356
let mut after: libc::sigaction = std::mem::zeroed();
343357
assert_eq!(
344358
libc::sigaction(libc::SIGINT, ptr::null(), &raw mut after),
@@ -351,6 +365,8 @@ fn child_only_sigint_suppression() {
351365

352366
#[test]
353367
fn failed_exec_exits_without_returning_into_the_host() {
368+
#[cfg(target_os = "macos")]
369+
let _fork_guard = FORK_LOCK.lock().unwrap();
354370
let fixture = Fixture::new();
355371
let invalid_program = fixture.path("not-an-executable-image");
356372
fs::write(&invalid_program, b"not an executable").unwrap();
@@ -384,7 +400,10 @@ fn failed_exec_exits_without_returning_into_the_host() {
384400
assert_eq!((input, output, error), (-1, -1, -1));
385401
let mut status = 0;
386402
assert_eq!(libc::waitpid(pid, &raw mut status, 0), pid);
387-
assert!(libc::WIFEXITED(status));
403+
assert!(
404+
libc::WIFEXITED(status),
405+
"child terminated abnormally (wait status {status:#x})"
406+
);
388407
assert_eq!(libc::WEXITSTATUS(status), libc::ENOEXEC);
389408
}
390409
}

0 commit comments

Comments
 (0)