Report a failed link when SIGCHLD is ignored

An ignored SIGCHLD is inherited across exec, e.g. from `trap '' CHLD`
in a shell. The child that fork_child creates is then reaped
automatically, so the parent's waitpid fails with ECHILD. The parent
ignored that and read the untouched zero status as a successful exit,
so a link that failed exited with status 0. Report a failure instead.

Also read and write the completion byte through File, which removes
two raw libc calls.

Based on https://github.com/rui314/mold/pull/1682.
This commit is contained in:
Rui Ueyama committed 2026-09-28 23:09:54 +09:00
1 parent fd13e83077
commit 25d2a3ecf1
2 files changed
+25 -9

No files matched your search

+14 -9
View File
@@ -2,7 +2,11 @@
//! handling for disk-full errors, and the `-run` subcommand.
#[cfg(not(windows))]
use std::os::unix::io::{AsRawFd, FromRawFd, OwnedFd};
use std::fs::File;
#[cfg(not(windows))]
use std::io::{Read, Write};
#[cfg(not(windows))]
use std::os::unix::io::{FromRawFd, OwnedFd};
#[cfg(not(windows))]
use std::sync::Mutex;
@@ -38,12 +42,17 @@ pub fn fork_child() {
if pid > 0 {
// Parent
drop(writer);
let mut buf = [0u8; 1];
if libc::read(reader.as_raw_fd(), buf.as_mut_ptr().cast(), 1) == 1 {
if File::from(reader).read_exact(&mut [0u8]).is_ok() {
libc::_exit(0);
}
// If SIGCHLD is ignored, which is inherited across exec, the
// child is reaped automatically and waitpid fails. Its exit
// status is lost then, so report a failure.
let mut status = 0;
libc::waitpid(pid, &raw mut status, 0);
if libc::waitpid(pid, &raw mut status, 0) == -1 {
libc::_exit(1);
}
if libc::WIFEXITED(status) {
libc::_exit(libc::WEXITSTATUS(status));
}
@@ -67,11 +76,7 @@ pub fn notify_parent() {
let Some(writer) = PIPE_WRITER.lock().unwrap().take() else {
return;
};
let buf = [1u8];
// SAFETY: writer owns a valid pipe write end.
unsafe {
libc::write(writer.as_raw_fd(), buf.as_ptr().cast(), 1);
}
let _ = File::from(writer).write_all(&[1]);
}
#[cfg(windows)]
+11
View File
@@ -0,0 +1,11 @@
#!/usr/bin/env bash
. $(dirname $0)/common.inc
cat <<EOF | $CC -o $t/a.o -c -xc -
int foo();
int main() { return foo(); }
EOF
# An ignored SIGCHLD is inherited across exec. The forked child is then
# reaped automatically, but mold must still report that the link failed.
(trap '' CHLD; not ./mold -o $t/exe $t/a.o)