#include <sys/cdefs.h>
__COPYRIGHT("@(#) Copyright (c) 2008\
The NetBSD Foundation, inc. All rights reserved.");
__RCSID("$NetBSD: t_setjmp.c,v 1.14 2026/03/18 18:24:59 skrll Exp $");
#include <sys/types.h>
#include <dlfcn.h>
#include <errno.h>
#include <setjmp.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <atf-c.h>
#include "h_macros.h"
enum test {
TEST_SETJMP,
TEST_U_SETJMP,
TEST_SIGSETJMP_SAVE,
TEST_SIGSETJMP_NOSAVE,
TEST_LONGJMP_ZERO,
TEST_U_LONGJMP_ZERO,
TEST_COMPAT13_SETJMP,
TEST_COMPAT13_SIGSETJMP_SAVE,
TEST_COMPAT13_SIGSETJMP_NOSAVE,
TEST_COMPAT13_LONGJMP_ZERO,
};
int (*compat13_sigsetjmp)(sigjmp_buf, int);
void (*compat13_siglongjmp)(sigjmp_buf, int);
int (*compat13_setjmp)(jmp_buf);
void (*compat13_longjmp)(jmp_buf, int);
static void
compatsigsys(int signo)
{
atf_tc_skip("no compat syscalls to test");
}
static void
compatsetup(void)
{
if ((compat13_sigsetjmp = dlsym(RTLD_SELF, "sigsetjmp")) == NULL ||
(compat13_siglongjmp = dlsym(RTLD_SELF, "siglongjmp")) == NULL ||
(compat13_setjmp = dlsym(RTLD_SELF, "setjmp")) == NULL ||
(compat13_longjmp = dlsym(RTLD_SELF, "longjmp")) == NULL)
atf_tc_skip("no compat functions to test");
REQUIRE_LIBC(signal(SIGSYS, &compatsigsys), SIG_ERR);
}
static int expectsignal;
static void
aborthandler(int signo __unused)
{
ATF_REQUIRE_MSG(expectsignal, "kill(SIGABRT) succeeded");
atf_tc_pass();
}
static void
h_check(enum test test)
{
struct sigaction sa;
jmp_buf jb;
sigjmp_buf sjb;
sigset_t ss;
int i, x;
volatile bool did_longjmp;
i = getpid();
did_longjmp = false;
switch (test) {
case TEST_COMPAT13_SETJMP:
case TEST_COMPAT13_SIGSETJMP_SAVE:
case TEST_COMPAT13_LONGJMP_ZERO:
case TEST_COMPAT13_SIGSETJMP_NOSAVE:
compatsetup();
break;
default:
break;
}
switch (test) {
case TEST_SETJMP:
case TEST_SIGSETJMP_SAVE:
case TEST_LONGJMP_ZERO:
case TEST_COMPAT13_SETJMP:
case TEST_COMPAT13_SIGSETJMP_SAVE:
case TEST_COMPAT13_LONGJMP_ZERO:
expectsignal = 0;
break;
case TEST_U_SETJMP:
case TEST_SIGSETJMP_NOSAVE:
case TEST_U_LONGJMP_ZERO:
case TEST_COMPAT13_SIGSETJMP_NOSAVE:
expectsignal = 1;
break;
default:
atf_tc_fail("unknown test");
}
sa.sa_handler = aborthandler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
RL(sigaction(SIGABRT, &sa, NULL));
RL(sigemptyset(&ss));
RL(sigaddset(&ss, SIGABRT));
RL(sigprocmask(SIG_BLOCK, &ss, NULL));
switch (test) {
case TEST_SETJMP:
case TEST_LONGJMP_ZERO:
x = setjmp(jb);
break;
case TEST_COMPAT13_SETJMP:
case TEST_COMPAT13_LONGJMP_ZERO:
x = (*compat13_setjmp)(jb);
break;
case TEST_U_SETJMP:
case TEST_U_LONGJMP_ZERO:
x = _setjmp(jb);
break;
case TEST_SIGSETJMP_SAVE:
case TEST_SIGSETJMP_NOSAVE:
x = sigsetjmp(sjb, !expectsignal);
break;
case TEST_COMPAT13_SIGSETJMP_SAVE:
case TEST_COMPAT13_SIGSETJMP_NOSAVE:
x = (*compat13_sigsetjmp)(sjb, !expectsignal);
break;
default:
atf_tc_fail("unknown test");
}
if (x != 0) {
switch (test) {
case TEST_LONGJMP_ZERO:
case TEST_U_LONGJMP_ZERO:
case TEST_COMPAT13_LONGJMP_ZERO:
ATF_REQUIRE_MSG(x == 1, "setjmp returned wrong value");
break;
default:
ATF_REQUIRE_MSG(x == i, "setjmp returned wrong value");
}
kill(i, SIGABRT);
ATF_REQUIRE_MSG(!expectsignal, "kill(SIGABRT) failed");
atf_tc_pass();
} else if (did_longjmp) {
atf_tc_fail("setjmp returned zero after longjmp");
}
RL(sigprocmask(SIG_UNBLOCK, &ss, NULL));
did_longjmp = true;
switch (test) {
case TEST_SETJMP:
longjmp(jb, i);
break;
case TEST_COMPAT13_SETJMP:
(*compat13_longjmp)(jb, i);
break;
case TEST_LONGJMP_ZERO:
longjmp(jb, 0);
break;
case TEST_COMPAT13_LONGJMP_ZERO:
(*compat13_longjmp)(jb, 0);
break;
case TEST_U_SETJMP:
_longjmp(jb, i);
break;
case TEST_U_LONGJMP_ZERO:
_longjmp(jb, 0);
break;
case TEST_SIGSETJMP_SAVE:
case TEST_SIGSETJMP_NOSAVE:
siglongjmp(sjb, i);
break;
case TEST_COMPAT13_SIGSETJMP_SAVE:
case TEST_COMPAT13_SIGSETJMP_NOSAVE:
(*compat13_siglongjmp)(sjb, i);
break;
default:
atf_tc_fail("unknown test");
}
atf_tc_fail("jmp failed");
}
ATF_TC(setjmp);
ATF_TC_HEAD(setjmp, tc)
{
atf_tc_set_md_var(tc, "descr", "Checks setjmp(3)");
}
ATF_TC_BODY(setjmp, tc)
{
h_check(TEST_SETJMP);
}
ATF_TC(_setjmp);
ATF_TC_HEAD(_setjmp, tc)
{
atf_tc_set_md_var(tc, "descr", "Checks _setjmp(3)");
}
ATF_TC_BODY(_setjmp, tc)
{
h_check(TEST_U_SETJMP);
}
ATF_TC(sigsetjmp_save);
ATF_TC_HEAD(sigsetjmp_save, tc)
{
atf_tc_set_md_var(tc, "descr",
"Checks sigsetjmp(3) with savemask enabled");
}
ATF_TC_BODY(sigsetjmp_save, tc)
{
h_check(TEST_SIGSETJMP_SAVE);
}
ATF_TC(sigsetjmp_nosave);
ATF_TC_HEAD(sigsetjmp_nosave, tc)
{
atf_tc_set_md_var(tc, "descr",
"Checks sigsetjmp(3) with savemask disabled");
}
ATF_TC_BODY(sigsetjmp_nosave, tc)
{
h_check(TEST_SIGSETJMP_NOSAVE);
}
ATF_TC(longjmp_zero);
ATF_TC_HEAD(longjmp_zero, tc)
{
atf_tc_set_md_var(tc, "descr",
"Checks longjmp(3) with a zero value");
}
ATF_TC_BODY(longjmp_zero, tc)
{
h_check(TEST_LONGJMP_ZERO);
}
ATF_TC(_longjmp_zero);
ATF_TC_HEAD(_longjmp_zero, tc)
{
atf_tc_set_md_var(tc, "descr",
"Checks _longjmp(3) with a zero value");
}
ATF_TC_BODY(_longjmp_zero, tc)
{
h_check(TEST_U_LONGJMP_ZERO);
}
ATF_TC(compat13_setjmp);
ATF_TC_HEAD(compat13_setjmp, tc)
{
atf_tc_set_md_var(tc, "descr", "Checks compat13 setjmp(3)");
}
ATF_TC_BODY(compat13_setjmp, tc)
{
h_check(TEST_COMPAT13_SETJMP);
}
ATF_TC(compat13_sigsetjmp_save);
ATF_TC_HEAD(compat13_sigsetjmp_save, tc)
{
atf_tc_set_md_var(tc, "descr",
"Checks compat13 sigsetjmp(3) with savemask enabled");
}
ATF_TC_BODY(compat13_sigsetjmp_save, tc)
{
h_check(TEST_COMPAT13_SIGSETJMP_SAVE);
}
ATF_TC(compat13_sigsetjmp_nosave);
ATF_TC_HEAD(compat13_sigsetjmp_nosave, tc)
{
atf_tc_set_md_var(tc, "descr",
"Checks compat13 sigsetjmp(3) with savemask disabled");
}
ATF_TC_BODY(compat13_sigsetjmp_nosave, tc)
{
h_check(TEST_COMPAT13_SIGSETJMP_NOSAVE);
}
ATF_TC(compat13_longjmp_zero);
ATF_TC_HEAD(compat13_longjmp_zero, tc)
{
atf_tc_set_md_var(tc, "descr",
"Checks compat13 longjmp(3) with a zero value");
}
ATF_TC_BODY(compat13_longjmp_zero, tc)
{
h_check(TEST_COMPAT13_LONGJMP_ZERO);
}
ATF_TP_ADD_TCS(tp)
{
ATF_TP_ADD_TC(tp, setjmp);
ATF_TP_ADD_TC(tp, _setjmp);
ATF_TP_ADD_TC(tp, sigsetjmp_save);
ATF_TP_ADD_TC(tp, sigsetjmp_nosave);
ATF_TP_ADD_TC(tp, longjmp_zero);
ATF_TP_ADD_TC(tp, _longjmp_zero);
ATF_TP_ADD_TC(tp, compat13_setjmp);
ATF_TP_ADD_TC(tp, compat13_sigsetjmp_save);
ATF_TP_ADD_TC(tp, compat13_sigsetjmp_nosave);
ATF_TP_ADD_TC(tp, compat13_longjmp_zero);
return atf_no_error();
}