#include <sys/cdefs.h>
__RCSID("$NetBSD: t_sig_backtrace.c,v 1.8 2025/04/17 14:18:40 riastradh Exp $");
#include <sys/mman.h>
#include <execinfo.h>
#include <setjmp.h>
#include <stdbool.h>
#include <signal.h>
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <atf-c.h>
stack_t sig_stack;
char *foo;
char *(*bar)(void);
static int the_loop_deref(int);
static int the_loop_jump(int);
#ifdef NOINLINE_HACK
volatile int noinline;
#endif
static int __noinline
func1(int i)
{
if (i > 100) {
return the_loop_deref(i);
}
return i + 1;
}
static int __noinline
func2(int i)
{
return func1(i) << 1;
}
static int __noinline
func3(int i)
{
if (func1(i) < 10) {
return func2(i);
} else {
return func1(i);
}
}
static int __noinline
the_loop_deref(int i0)
{
volatile int i = i0;
while (*foo != 0) {
i = func3(i);
i = func1(i);
i = func2(i);
}
#ifdef NOINLINE_HACK
if (noinline)
vfork();
#endif
return i;
}
static int __noinline
the_loop_jump(int i0)
{
volatile int i = i0;
while ((*bar)() != 0) {
i = func3(i);
i = func1(i);
i = func2(i);
}
#ifdef NOINLINE_HACK
if (noinline)
vfork();
#endif
return i;
}
jmp_buf env;
static void
handler(int s)
{
printf("signal: %d\n", s);
void *array[10];
size_t size = backtrace(array, 10);
ATF_REQUIRE(size != 0);
printf("Backtrace %zd stack frames.\n", size);
fflush(stdout);
backtrace_symbols_fd(array, size, STDOUT_FILENO);
char **strings = backtrace_symbols_fmt(array, size, "%n");
bool found_handler = false;
bool found_sigtramp = false;
bool found_the_loop = false;
bool found_main = false;
size_t i;
for (i = 0; i < size; i++) {
if (!found_handler &&
strcmp(strings[i], "handler") == 0) {
found_handler = true;
continue;
}
if (found_handler && !found_sigtramp &&
strncmp(strings[i], "__sigtramp_siginfo_",
strlen("__sigtramp_siginfo_")) == 0) {
found_sigtramp = true;
continue;
}
if (found_sigtramp && !found_the_loop &&
strncmp(strings[i], "the_loop", strlen("the_loop")) == 0) {
found_the_loop = true;
continue;
}
if (found_the_loop && !found_main &&
strcmp(strings[i], "atf_tp_main") == 0) {
found_main = true;
break;
}
}
ATF_CHECK(found_handler);
ATF_CHECK(found_sigtramp);
ATF_CHECK(found_the_loop);
ATF_CHECK(found_main);
longjmp(env, 1);
}
ATF_TC(sig_backtrace_deref);
ATF_TC_HEAD(sig_backtrace_deref, tc)
{
atf_tc_set_md_var(tc, "descr",
"Test backtrace(3) across signal handlers, null pointer deref");
}
ATF_TC_BODY(sig_backtrace_deref, tc)
{
sig_stack.ss_sp = mmap(NULL, SIGSTKSZ, PROT_READ | PROT_WRITE,
MAP_ANON | MAP_STACK, -1, 0);
ATF_REQUIRE(sig_stack.ss_sp != MAP_FAILED);
sig_stack.ss_size = SIGSTKSZ;
sig_stack.ss_flags = 0;
ATF_REQUIRE(sigaltstack(&sig_stack, NULL) == 0);
struct sigaction sa = {
.sa_handler = handler,
.sa_flags = SA_ONSTACK,
};
ATF_REQUIRE(sigaction(SIGSEGV, &sa, NULL) == 0);
#ifdef __sparc__
atf_tc_expect_fail("PR port-sparc64/59313:"
" t_sig_backtrace tests are failing");
#endif
if (setjmp(env) == 0) {
printf("%d\n", the_loop_deref(0));
}
}
ATF_TC(sig_backtrace_jump);
ATF_TC_HEAD(sig_backtrace_jump, tc)
{
atf_tc_set_md_var(tc, "descr",
"Test backtrace(3) across signal handlers, null pointer jump");
}
ATF_TC_BODY(sig_backtrace_jump, tc)
{
sig_stack.ss_sp = mmap(NULL, SIGSTKSZ, PROT_READ | PROT_WRITE,
MAP_ANON | MAP_STACK, -1, 0);
ATF_REQUIRE(sig_stack.ss_sp != MAP_FAILED);
sig_stack.ss_size = SIGSTKSZ;
sig_stack.ss_flags = 0;
ATF_REQUIRE(sigaltstack(&sig_stack, NULL) == 0);
struct sigaction sa = {
.sa_handler = handler,
.sa_flags = SA_ONSTACK,
};
ATF_REQUIRE(sigaction(SIGSEGV, &sa, NULL) == 0);
atf_tc_expect_fail("PR lib/56940");
if (setjmp(env) == 0) {
printf("%d\n", the_loop_jump(0));
}
}
ATF_TP_ADD_TCS(tp)
{
ATF_TP_ADD_TC(tp, sig_backtrace_deref);
ATF_TP_ADD_TC(tp, sig_backtrace_jump);
return atf_no_error();
}