#include <sys/cdefs.h>
__RCSID("$NetBSD: t_execregs.c,v 1.7 2025/04/27 14:30:03 riastradh Exp $");
#include <sys/wait.h>
#include <atf-c.h>
#include <err.h>
#include <inttypes.h>
#include <limits.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#ifdef HAVE_EXECREGS_TEST
#include "execregs.h"
#include "isqemu.h"
#include "h_macros.h"
static void
readregs(int rfd, register_t regs[static NEXECREGS])
{
uint8_t *p;
size_t n;
ssize_t nread;
p = (void *)regs;
n = NEXECREGS*sizeof(regs[0]);
while (n) {
RL(nread = read(rfd, p, n));
ATF_CHECK_MSG((size_t)nread <= n,
"overlong read: %zu > %zu", (size_t)nread, n);
if (nread == 0)
break;
p += (size_t)nread;
n -= (size_t)nread;
}
ATF_CHECK_EQ_MSG(n, 0,
"truncated read, missing %zu of %zu bytes",
n, NEXECREGS*sizeof(regs[0]));
}
static void
checkregs(const register_t regs[static NEXECREGS])
{
unsigned i;
#ifdef __hppa__
if (isQEMU()) {
atf_tc_expect_fail("PR port-hppa/59114: hppa:"
" eager fpu switching for qemu and/or spectre mitigation");
}
#endif
for (i = 0; i < NEXECREGS; i++) {
if (regs[i] != 0) {
for (i = 0; i < NEXECREGS; i++) {
fprintf(stderr, "[%s] %"PRIxREGISTER"\n",
regname[i], regs[i]);
}
fprintf(stderr, "\n");
atf_tc_fail("registers not zeroed");
}
}
}
static void
testregs(int child, const int pipefd[static 2],
register_t regs[static NEXECREGS])
{
int status;
RL(close(pipefd[1]));
readregs(pipefd[0], regs);
RL(waitpid(child, &status, 0));
if (WIFSIGNALED(status)) {
atf_tc_fail_nonfatal("child terminated on signal %d (%s)",
WTERMSIG(status), strsignal(WTERMSIG(status)));
} else if (!WIFEXITED(status)) {
atf_tc_fail_nonfatal("child terminated mysteriously,"
" status=0x%x",
status);
} else {
ATF_CHECK_MSG(WEXITSTATUS(status) == 0,
"child exited with code %d", WEXITSTATUS(status));
}
checkregs(regs);
}
#endif
ATF_TC(execregszero);
ATF_TC_HEAD(execregszero, tc)
{
atf_tc_set_md_var(tc, "descr",
"Test execve(2) zeroes registers");
}
ATF_TC_BODY(execregszero, tc)
{
#ifdef HAVE_EXECREGS_TEST
char h_execregs[PATH_MAX];
int pipefd[2];
register_t regs[NEXECREGS];
pid_t child;
RL(snprintf(h_execregs, sizeof(h_execregs), "%s/h_execregs",
atf_tc_get_config_var(tc, "srcdir")));
RL(pipe(pipefd));
memset(regs, 0x5a, sizeof(regs));
RL(child = fork());
if (child == 0) {
if (dup2(pipefd[1], STDOUT_FILENO) == -1)
err(1, "dup2");
if (closefrom(STDERR_FILENO + 1) == -1)
err(1, "closefrom");
if (execregschild(h_execregs) == -1)
err(1, "execve");
_exit(2);
}
testregs(child, pipefd, regs);
#else
atf_tc_skip("missing test for PR kern/59084:"
" exec/spawn leaks register content");
#endif
}
ATF_TC(spawnregszero);
ATF_TC_HEAD(spawnregszero, tc)
{
atf_tc_set_md_var(tc, "descr",
"Test posix_spawn(2) zeroes registers");
}
ATF_TC_BODY(spawnregszero, tc)
{
#ifdef HAVE_EXECREGS_TEST
char h_execregs[PATH_MAX];
int pipefd[2];
register_t regs[NEXECREGS];
pid_t child;
RL(snprintf(h_execregs, sizeof(h_execregs), "%s/h_execregs",
atf_tc_get_config_var(tc, "srcdir")));
RL(pipe(pipefd));
memset(regs, 0x5a, sizeof(regs));
RL(child = spawnregschild(h_execregs, pipefd[1]));
testregs(child, pipefd, regs);
#else
atf_tc_skip("missing test for PR kern/59084:"
" exec/spawn leaks register content");
#endif
}
ATF_TP_ADD_TCS(tp)
{
ATF_TP_ADD_TC(tp, execregszero);
ATF_TP_ADD_TC(tp, spawnregszero);
return atf_no_error();
}