mirror of
https://github.com/HeyPuter/gex.git
synced 2026-11-13 20:12:43 -10:00
native: only crash signals are said by default; GEX_LOG_CHILD_SIGNALS=1 says every one
A pipeline's SIGPIPE ends a child on every `yes | head`, and the session ends its processes with SIGTERM: neither is said unless asked for. Said by default: SIGILL, SIGTRAP, SIGABRT, SIGBUS, SIGFPE, SIGSEGV, SIGSYS and SIGKILL (gex never ends a process with SIGKILL itself), and any gex limitation delivered as a signal. tests/child-signal.sh checks both modes, with a SIGSEGV child and a child another process kills, on a64 and x86. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_c8c04c00da9a4525ab21b0b9f146c598
This commit is contained in:
1 parent
c8853f7ade
commit
4c36082b27
2 files changed
+56
-39
No files matched your search
+10
-6
@@ -2722,14 +2722,18 @@ fn fork_process(store: &impl wasmtime::AsContext<Data = HostState>, req: ForkReq
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// A child ended by a signal says so, once, as a shell would: a process
|
||||
/// gone with no word otherwise (one whose own crash handler does not catch
|
||||
/// the signal) leaves only its absence. Not the signals a session ends its
|
||||
/// processes with.
|
||||
/// A child ended by a crash signal says so, once, as a shell would: one no
|
||||
/// crash handler catches otherwise leaves only its absence. A gex limitation
|
||||
/// delivered as a signal is said whatever the signal. The rest (a pipeline's
|
||||
/// SIGPIPE, the SIGTERM a session ends its processes with) only under
|
||||
/// `GEX_LOG_CHILD_SIGNALS=1`. gex itself never ends a process with SIGKILL,
|
||||
/// so a SIGKILL is another process's doing and is said.
|
||||
fn say_killed(pid: i32, exit: &Exit) {
|
||||
let Exit::Signal { signal, pc, detail, internal } = exit else { return };
|
||||
// SIGHUP, SIGINT, SIGTERM.
|
||||
if !internal && matches!(signal, 1 | 2 | 15) {
|
||||
static ALL: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
|
||||
let all = *ALL.get_or_init(|| std::env::var("GEX_LOG_CHILD_SIGNALS").is_ok_and(|v| v == "1"));
|
||||
// SIGILL, SIGTRAP, SIGABRT, SIGBUS, SIGFPE, SIGKILL, SIGSEGV, SIGSYS.
|
||||
if !internal && !all && !matches!(signal, 4 | 5 | 6 | 7 | 8 | 9 | 11 | 31) {
|
||||
return;
|
||||
}
|
||||
let detail = if detail.is_empty() || detail.starts_with("killed by signal") { String::new() } else { format!(": {detail}") };
|
||||
|
||||
+46
-33
@@ -1,8 +1,9 @@
|
||||
#!/bin/sh
|
||||
# A child process killed by a signal says so on gex's stderr, as a shell
|
||||
# would: one no crash handler catches (a SIGPIPE, a SIGKILL, an abort with
|
||||
# none installed) otherwise left only its absence. A child ended
|
||||
# by SIGTERM, as a session ends its processes, says nothing.
|
||||
# A child process killed by a crash signal says so on gex's stderr, as a
|
||||
# shell would: one no crash handler catches (an abort with none installed, a
|
||||
# SIGSEGV, a SIGKILL from another process) otherwise left only its absence.
|
||||
# A pipeline's SIGPIPE and a session's SIGTERM say nothing, unless
|
||||
# GEX_LOG_CHILD_SIGNALS=1 asks for every signal.
|
||||
#
|
||||
# tests/child-signal.sh [a64|x86] (needs zig and the guest's gexc)
|
||||
set -eu
|
||||
@@ -20,43 +21,55 @@ mkdir -p "$WORK"
|
||||
[ -n "${KEEP:-}" ] || trap 'rm -rf "$WORK"' EXIT
|
||||
cat > "$WORK/cs.c" <<'EOF2'
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
static int child(int how) {
|
||||
pid_t p = fork();
|
||||
if (p == 0) {
|
||||
if (how == 0) { int fd[2]; pipe(fd); close(fd[0]); write(fd[1], "x", 1); }
|
||||
if (how == 1) abort();
|
||||
if (how == 3) kill(getpid(), SIGKILL);
|
||||
if (how == 2) raise(SIGTERM);
|
||||
_exit(0);
|
||||
}
|
||||
int st = 0;
|
||||
waitpid(p, &st, 0);
|
||||
printf("child %d: pid %d signal %d\n", how, p, WIFSIGNALED(st) ? WTERMSIG(st) : 0);
|
||||
fflush(stdout);
|
||||
return p;
|
||||
}
|
||||
int main(void) {
|
||||
for (int how = 0; how < 4; how++) child(how);
|
||||
for (int how = 0; how < 4; how++) {
|
||||
pid_t p = fork();
|
||||
if (p == 0) {
|
||||
if (how == 0) { int fd[2]; pipe(fd); close(fd[0]); write(fd[1], "x", 1); }
|
||||
if (how == 1) abort();
|
||||
if (how == 2) raise(SIGTERM);
|
||||
if (how == 3) raise(SIGSEGV);
|
||||
_exit(0);
|
||||
}
|
||||
if (p > 0 && how == 3) {
|
||||
/* and one another process kills */
|
||||
pid_t q = fork();
|
||||
if (q == 0) { for (;;) pause(); }
|
||||
kill(q, SIGKILL);
|
||||
int st = 0;
|
||||
waitpid(q, &st, 0);
|
||||
printf("child 4: pid %d signal %d\n", q, WIFSIGNALED(st) ? WTERMSIG(st) : 0);
|
||||
}
|
||||
int st = 0;
|
||||
waitpid(p, &st, 0);
|
||||
printf("child %d: pid %d signal %d\n", how, p, WIFSIGNALED(st) ? WTERMSIG(st) : 0);
|
||||
fflush(stdout);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
EOF2
|
||||
zig cc -target "$TARGET" -O1 -static -Wl,--image-base=0x1000000 -o "$WORK/cs" "$WORK/cs.c"
|
||||
timeout "${TMO:-120}" "$GEXC" run "$WORK/cs" > "$WORK/out" 2> "$WORK/err" || true
|
||||
fail=0
|
||||
for want in "0 13 yes" "1 6 yes" "2 15 no" "3 9 yes"; do
|
||||
set -- $want
|
||||
line=$(grep "^child $1:" "$WORK/out" || true)
|
||||
pid=$(echo "$line" | sed -n 's/.* pid \([0-9]*\) .*/\1/p')
|
||||
sig=$(echo "$line" | sed -n 's/.* signal \([0-9]*\)$/\1/p')
|
||||
said=no; [ -n "$pid" ] && grep -q "pid $pid killed by signal $2 " "$WORK/err" && said=yes
|
||||
if [ "$sig" = "$2" ] && [ "$said" = "$3" ]; then :; else
|
||||
echo "FAIL child-signal $ARCH: child $1 got signal '${sig:-?}' (want $2), said: $said (want $3)"
|
||||
fail=1
|
||||
fi
|
||||
# Each case: child, its signal, whether it is said by default, and with every signal asked for.
|
||||
for mode in default all; do
|
||||
if [ $mode = all ]; then export GEX_LOG_CHILD_SIGNALS=1; else unset GEX_LOG_CHILD_SIGNALS; fi
|
||||
timeout "${TMO:-120}" "$GEXC" run "$WORK/cs" > "$WORK/out.$mode" 2> "$WORK/err.$mode" || true
|
||||
for want in "0 13 no yes" "1 6 yes yes" "2 15 no yes" "3 11 yes yes" "4 9 yes yes"; do
|
||||
set -- $want
|
||||
expect=$3; [ $mode = all ] && expect=$4
|
||||
line=$(grep "^child $1:" "$WORK/out.$mode" || true)
|
||||
pid=$(echo "$line" | sed -n 's/.* pid \([0-9]*\) .*/\1/p')
|
||||
sig=$(echo "$line" | sed -n 's/.* signal \([0-9]*\)$/\1/p')
|
||||
said=no; [ -n "$pid" ] && grep -q "pid $pid killed by signal $2 " "$WORK/err.$mode" && said=yes
|
||||
if [ "$sig" != "$2" ] || [ "$said" != "$expect" ]; then
|
||||
echo "FAIL child-signal $ARCH ($mode): child $1 got signal '${sig:-?}' (want $2), said: $said (want $expect)"
|
||||
fail=1
|
||||
fi
|
||||
done
|
||||
done
|
||||
[ $fail = 0 ] && echo "ok child-signal $ARCH: SIGPIPE, SIGABRT and SIGKILL children named, SIGTERM quiet"
|
||||
[ $fail = 0 ] && echo "ok child-signal $ARCH: SIGABRT, SIGSEGV and SIGKILL children named, SIGPIPE and SIGTERM only when asked"
|
||||
exit $fail
|
||||
Reference in new issue
Block a user