Diagnose Guile subprocess crash on FreeBSD
This commit is contained in:
134
tests/guile/posix-spawn-freebsd-diagnostics.c
Normal file
134
tests/guile/posix-spawn-freebsd-diagnostics.c
Normal file
@@ -0,0 +1,134 @@
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <spawn.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
extern char **environ;
|
||||
|
||||
static int native_spawn_closefrom_test(void) {
|
||||
posix_spawn_file_actions_t actions;
|
||||
pid_t pid;
|
||||
int status;
|
||||
char *argv[] = { "/usr/bin/true", NULL };
|
||||
|
||||
if (posix_spawn_file_actions_init(&actions) != 0)
|
||||
return 1;
|
||||
if (posix_spawn_file_actions_adddup2(&actions, STDIN_FILENO, STDIN_FILENO) != 0)
|
||||
return 2;
|
||||
if (posix_spawn_file_actions_adddup2(&actions, STDOUT_FILENO, STDOUT_FILENO) != 0)
|
||||
return 3;
|
||||
if (posix_spawn_file_actions_adddup2(&actions, STDERR_FILENO, STDERR_FILENO) != 0)
|
||||
return 4;
|
||||
if (posix_spawn_file_actions_addclosefrom_np(&actions, 3) != 0)
|
||||
return 5;
|
||||
if (posix_spawn(&pid, "/usr/bin/true", &actions, NULL, argv, environ) != 0)
|
||||
return 6;
|
||||
if (waitpid(pid, &status, 0) < 0)
|
||||
return 7;
|
||||
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
|
||||
return 8;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adddup2_invalid_fd_accepted(void) {
|
||||
posix_spawn_file_actions_t actions;
|
||||
if (posix_spawn_file_actions_init(&actions) != 0)
|
||||
return -1;
|
||||
return posix_spawn_file_actions_adddup2(&actions, 10000000, 2) == 0;
|
||||
}
|
||||
|
||||
static int addopen_invalid_fd_accepted(void) {
|
||||
posix_spawn_file_actions_t actions;
|
||||
if (posix_spawn_file_actions_init(&actions) != 0)
|
||||
return -1;
|
||||
return posix_spawn_file_actions_addopen(&actions, 10000000, "foo", 0, O_RDONLY) == 0;
|
||||
}
|
||||
|
||||
static int write_script(char *path, size_t size) {
|
||||
int fd;
|
||||
const char script[] = ":\n";
|
||||
|
||||
if (snprintf(path, size, "/tmp/fruix-posix-spawn-script-XXXXXX") >= (int)size)
|
||||
return -1;
|
||||
fd = mkstemp(path);
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
if (write(fd, script, sizeof(script) - 1) != (ssize_t)(sizeof(script) - 1)) {
|
||||
close(fd);
|
||||
unlink(path);
|
||||
return -1;
|
||||
}
|
||||
if (fchmod(fd, 0700) != 0) {
|
||||
close(fd);
|
||||
unlink(path);
|
||||
return -1;
|
||||
}
|
||||
if (close(fd) != 0) {
|
||||
unlink(path);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int secure_exec_result(int use_path) {
|
||||
char script[128];
|
||||
pid_t child;
|
||||
int err;
|
||||
int status = 0;
|
||||
char *argv[] = { script, NULL };
|
||||
char *env[] = { "PATH=/tmp:/usr/bin:/bin", NULL };
|
||||
|
||||
if (write_script(script, sizeof(script)) != 0)
|
||||
return -1;
|
||||
|
||||
err = use_path
|
||||
? posix_spawnp(&child, script, NULL, NULL, argv, env)
|
||||
: posix_spawn(&child, script, NULL, NULL, argv, env);
|
||||
|
||||
if (err == ENOEXEC) {
|
||||
unlink(script);
|
||||
return 0;
|
||||
}
|
||||
if (err != 0) {
|
||||
unlink(script);
|
||||
return 1;
|
||||
}
|
||||
while (waitpid(child, &status, 0) != child)
|
||||
;
|
||||
unlink(script);
|
||||
if (!WIFEXITED(status))
|
||||
return 2;
|
||||
if (WEXITSTATUS(status) != 127)
|
||||
return 3;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
int native_ok = native_spawn_closefrom_test();
|
||||
int dup2_broken = adddup2_invalid_fd_accepted();
|
||||
int addopen_broken = addopen_invalid_fd_accepted();
|
||||
int spawn_secure = secure_exec_result(0);
|
||||
int spawnp_secure = secure_exec_result(1);
|
||||
int issue_profile_match =
|
||||
native_ok == 0 &&
|
||||
dup2_broken == 1 &&
|
||||
addopen_broken == 1 &&
|
||||
spawn_secure == 0 &&
|
||||
spawnp_secure == 3;
|
||||
|
||||
printf("native-spawn-closefrom=%s\n", native_ok == 0 ? "ok" : "fail");
|
||||
printf("adddup2-invalid-fd-accepted=%s\n", dup2_broken == 1 ? "yes" : (dup2_broken == 0 ? "no" : "error"));
|
||||
printf("addopen-invalid-fd-accepted=%s\n", addopen_broken == 1 ? "yes" : (addopen_broken == 0 ? "no" : "error"));
|
||||
printf("posix_spawn-secure-exec-result=%d\n", spawn_secure);
|
||||
printf("posix_spawnp-secure-exec-result=%d\n", spawnp_secure);
|
||||
printf("issue-profile-match=%s\n", issue_profile_match ? "yes" : "no");
|
||||
|
||||
return issue_profile_match ? 0 : 1;
|
||||
}
|
||||
42
tests/guile/run-subprocess-diagnostics.sh
Executable file
42
tests/guile/run-subprocess-diagnostics.sh
Executable file
@@ -0,0 +1,42 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
script_dir=$(CDPATH= cd -- "$(dirname "$0")" && pwd)
|
||||
repo_root=$(CDPATH= cd -- "$script_dir/../.." && pwd)
|
||||
workdir=$(mktemp -d /tmp/fruix-guile-subprocess.XXXXXX)
|
||||
trap 'rm -rf "$workdir"' EXIT INT TERM
|
||||
|
||||
if command -v guile3 >/dev/null 2>&1; then
|
||||
guile_bin=$(command -v guile3)
|
||||
elif command -v guile-3.0 >/dev/null 2>&1; then
|
||||
guile_bin=$(command -v guile-3.0)
|
||||
else
|
||||
echo "Unable to find guile3 or guile-3.0 in PATH" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ulimit -c 0 || true
|
||||
|
||||
cc -Wall -Wextra -O2 "$repo_root/tests/guile/posix-spawn-freebsd-diagnostics.c" \
|
||||
-o "$workdir/posix-spawn-freebsd-diagnostics"
|
||||
|
||||
printf '== Native posix_spawn diagnostics ==\n'
|
||||
"$workdir/posix-spawn-freebsd-diagnostics"
|
||||
|
||||
run_guile_case() {
|
||||
name=$1
|
||||
code=$2
|
||||
set +e
|
||||
"$guile_bin" -c "$code" >/dev/null 2>&1
|
||||
rc=$?
|
||||
set -e
|
||||
printf '%s exit=%s\n' "$name" "$rc"
|
||||
[ "$rc" -eq 139 ]
|
||||
}
|
||||
|
||||
printf '== Guile subprocess crash repro ==\n'
|
||||
run_guile_case system-star '(system* "/usr/bin/true")'
|
||||
run_guile_case spawn '(spawn "/usr/bin/true" (list "/usr/bin/true"))'
|
||||
run_guile_case open-pipe-star '(use-modules (ice-9 popen)) (open-pipe* OPEN_READ "/usr/bin/true")'
|
||||
|
||||
printf 'known FreeBSD Guile subprocess crash profile reproduced\n'
|
||||
Reference in New Issue
Block a user