diff --git a/tests/sys/kern/sigwait.c b/tests/sys/kern/sigwait.c index 0b6e078b6548..960a315cafce 100644 --- a/tests/sys/kern/sigwait.c +++ b/tests/sys/kern/sigwait.c @@ -1,584 +1,585 @@ /*- * Copyright (c) 2022 Dmitry Chagin * * SPDX-License-Identifier: BSD-2-Clause */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include static inline struct timespec make_timespec(time_t s, long int ns) { struct timespec rts; rts.tv_sec = s; rts.tv_nsec = ns; return (rts); } static void dummy_sig_handler(int sig) { } static void dummy_sigchld(int signo, siginfo_t *info, void *ctx) { } static void support_signal(int sig, sig_t handler) { ATF_REQUIRE(signal(sig, handler) != SIG_ERR); } static void support_sysctlset(const char *name, int32_t val) { ATF_REQUIRE(sysctlbyname(name, NULL, NULL, &val, sizeof(val)) == 0); } static timer_t support_create_timer(uint64_t sec, long int nsec, bool repeat, bool callback) { struct sigevent ev = { .sigev_notify = SIGEV_SIGNAL, .sigev_signo = SIGALRM }; struct itimerspec its = { { .tv_sec = repeat ? sec : 0, .tv_nsec = repeat ? nsec : 0 }, { .tv_sec = sec, .tv_nsec = nsec } }; struct sigaction sa; timer_t timerid; if (callback) { sa.sa_handler = dummy_sig_handler; sigemptyset (&sa.sa_mask); sa.sa_flags = 0; ATF_REQUIRE(sigaction(SIGALRM, &sa, NULL) == 0); } ATF_REQUIRE(timer_create(CLOCK_REALTIME, &ev, &timerid) == 0); ATF_REQUIRE(timer_settime(timerid, 0, &its, NULL) == 0); return (timerid); } static void support_delete_timer(timer_t timer) { ATF_REQUIRE(timer_delete(timer) == 0); support_signal(SIGALRM, SIG_DFL); } static pid_t support_create_sig_proc(int sig, int count, unsigned int usec) { pid_t pid, cpid; pid = getpid(); ATF_REQUIRE(pid > 0); cpid = fork(); ATF_REQUIRE(cpid >= 0); if (cpid == 0) { while (count-- > 0) { usleep(usec); if (kill(pid, sig) == -1) break; } exit(0); } return (cpid); } #define TIMESPEC_HZ 1000000000 static void test_sigtimedwait_timeout_eagain(time_t sec, bool zero_tmo) { struct timespec ts, timeout, now; sigset_t ss; int rv; ATF_REQUIRE(clock_gettime(CLOCK_REALTIME, &ts) == 0); timeout = make_timespec(sec, zero_tmo ? 0 : TIMESPEC_HZ/2); timespecadd(&ts, &timeout, &ts); sigemptyset(&ss); sigaddset(&ss, SIGUSR1); rv = sigtimedwait(&ss, NULL, &timeout); ATF_REQUIRE_EQ_MSG(-1, rv, "sigtimedwait () should fail: rv %d, errno %d", rv, errno); ATF_REQUIRE_EQ_MSG(EAGAIN, errno, "sigtimedwait() should fail with EAGAIN: rv %d, errno %d", rv, errno); /* now >= ts */ ATF_REQUIRE(clock_gettime(CLOCK_REALTIME, &now) == 0); ATF_REQUIRE_MSG(timespeccmp(&now, &ts, >=) == true, - "timespeccmp: now { %ld.%ld } < ts { %ld.%ld }", - now.tv_sec, now.tv_nsec, ts.tv_sec, ts.tv_nsec); + "timespeccmp: now { %jd.%ld } < ts { %jd.%ld }", + (intmax_t)now.tv_sec, now.tv_nsec, + (intmax_t)ts.tv_sec, ts.tv_nsec); } ATF_TC(test_sigtimedwait_timeout_eagain0); ATF_TC_HEAD(test_sigtimedwait_timeout_eagain0, tc) { atf_tc_set_md_var(tc, "descr", "Check if sigtimedwait exits immediately"); } ATF_TC_BODY(test_sigtimedwait_timeout_eagain0, tc) { test_sigtimedwait_timeout_eagain(0, true); } ATF_TC(test_sigtimedwait_timeout_eagain1); ATF_TC_HEAD(test_sigtimedwait_timeout_eagain1, tc) { atf_tc_set_md_var(tc, "descr", "Check if sigtimedwait exits immediately"); } ATF_TC_BODY(test_sigtimedwait_timeout_eagain1, tc) { test_sigtimedwait_timeout_eagain(-1, true); } ATF_TC(test_sigtimedwait_timeout_eagain2); ATF_TC_HEAD(test_sigtimedwait_timeout_eagain2, tc) { atf_tc_set_md_var(tc, "descr", "Check if sigtimedwait exits immediately"); } ATF_TC_BODY(test_sigtimedwait_timeout_eagain2, tc) { test_sigtimedwait_timeout_eagain(-1, false); } ATF_TC(test_sigtimedwait_timeout_eagain3); ATF_TC_HEAD(test_sigtimedwait_timeout_eagain3, tc) { atf_tc_set_md_var(tc, "descr", "Check if sigtimedwait exits after specified timeout"); } ATF_TC_BODY(test_sigtimedwait_timeout_eagain3, tc) { test_sigtimedwait_timeout_eagain(0, false); } ATF_TC(test_sigtimedwait_large_timeout_eintr); ATF_TC_HEAD(test_sigtimedwait_large_timeout_eintr, tc) { atf_tc_set_md_var(tc, "descr", "Check if sigtimedwait exits with EINTR"); } ATF_TC_BODY(test_sigtimedwait_large_timeout_eintr, tc) { struct timespec ts; timer_t timerid; sigset_t ss; int rv; ts = make_timespec(LONG_MAX, 0); timerid = support_create_timer(0, 100000000, false, true); sigemptyset(&ss); sigaddset(&ss, SIGUSR1); rv = sigtimedwait(&ss, NULL, &ts); ATF_REQUIRE_EQ_MSG(-1, rv, "sigtimedwait () should fail: rv %d, errno %d", rv, errno); ATF_REQUIRE_EQ_MSG(EINTR, errno, "sigtimedwait() should fail with EINTR: rv %d, errno %d", rv, errno); support_delete_timer(timerid); } ATF_TC(test_sigtimedwait_infinity); ATF_TC_HEAD(test_sigtimedwait_infinity, tc) { atf_tc_set_md_var(tc, "descr", "Check if sigtimedwait exits with EINTR"); } ATF_TC_BODY(test_sigtimedwait_infinity, tc) { timer_t timerid; sigset_t ss; int rv; timerid = support_create_timer(0, 100000000, false, true); sigemptyset(&ss); sigaddset(&ss, SIGUSR1); rv = sigtimedwait(&ss, NULL, NULL); ATF_REQUIRE_EQ_MSG(-1, rv, "sigtimedwait () should fail: rv %d, errno %d", rv, errno); ATF_REQUIRE_EQ_MSG(EINTR, errno, "sigtimedwait() should fail with EINTR: rv %d, errno %d", rv, errno); support_delete_timer(timerid); } ATF_TC(test_sigtimedwait_einval); ATF_TC_HEAD(test_sigtimedwait_einval, tc) { atf_tc_set_md_var(tc, "descr", "Check if sigtimedwait exits with EINVAL"); } ATF_TC_BODY(test_sigtimedwait_einval, tc) { struct timespec ts; timer_t timerid; sigset_t ss; int rv; ts = make_timespec(0, -1); timerid = support_create_timer(0, 100000000, false, true); sigemptyset(&ss); sigaddset(&ss, SIGUSR1); rv = sigtimedwait(&ss, NULL, &ts); ATF_REQUIRE_EQ_MSG(-1, rv, "sigtimedwait () should fail: rv %d, errno %d", rv, errno); ATF_REQUIRE_EQ_MSG(EINVAL, errno, "sigtimedwait() should fail with EINVAL: rv %d, errno %d", rv, errno); support_delete_timer(timerid); } ATF_TC(test_sigwait_eintr); ATF_TC_HEAD(test_sigwait_eintr, tc) { atf_tc_set_md_var(tc, "descr", "Check if sigwait exits with EINTR"); } ATF_TC_BODY(test_sigwait_eintr, tc) { timer_t timerid; sigset_t ss; int rv, sig, pid; support_signal(SIGUSR1, dummy_sig_handler); pid = support_create_sig_proc(SIGUSR1, 1, 400000); timerid = support_create_timer(0, 200000, false, true); sigemptyset(&ss); sigaddset(&ss, SIGUSR1); rv = sigwait(&ss, &sig); ATF_REQUIRE_EQ_MSG(0, rv, "sigwait() should not fail: rv %d, errno %d", rv, errno); ATF_REQUIRE_EQ_MSG(SIGUSR1, sig, "sigwait() should return SIGUSR1: rv %d, sig %d", rv, sig); ATF_REQUIRE(waitid(P_PID, pid, NULL, WEXITED) == 0); support_delete_timer(timerid); } ATF_TC(test_sigwaitinfo_eintr); ATF_TC_HEAD(test_sigwaitinfo_eintr, tc) { atf_tc_set_md_var(tc, "descr", "Check if sigwaitinfo exits with EINTR"); } ATF_TC_BODY(test_sigwaitinfo_eintr, tc) { timer_t timerid; sigset_t ss; int rv; timerid = support_create_timer(0, 100000000, false, true); sigemptyset(&ss); sigaddset(&ss, SIGUSR1); rv = sigwaitinfo(&ss, NULL); ATF_REQUIRE_EQ_MSG(-1, rv, "sigwaitinfo() should fail, rv %d != -1", rv); ATF_REQUIRE_EQ_MSG(EINTR, errno, "sigwaitinfo() should fail errno %d != EINTR", errno); support_delete_timer(timerid); } /* * Test kern.sig_discard_ign knob (default true). * See commit bc387624 */ static void test_sig_discard_ign(bool ignore) { struct timespec ts; sigset_t mask; pid_t pid; int rv; support_signal(SIGUSR2, SIG_IGN); if (ignore) support_sysctlset("kern.sig_discard_ign", 1); else support_sysctlset("kern.sig_discard_ign", 0); sigemptyset(&mask); sigaddset(&mask, SIGUSR2); ATF_REQUIRE(sigprocmask(SIG_SETMASK, &mask, NULL) == 0); pid = support_create_sig_proc(SIGUSR2, 1, 100000); ts = make_timespec(1, 0); rv = sigtimedwait(&mask, NULL, &ts); if (ignore == true) { ATF_REQUIRE_EQ_MSG(-1, rv, "sigtimedwait() ign=on should fail, rv %d != -1", rv); ATF_REQUIRE_EQ_MSG(EAGAIN, errno, "sigtimedwait() ign=on should fail with EAGAIN errno %d", errno); } else ATF_REQUIRE_EQ_MSG(SIGUSR2, rv, "sigtimedwait() ign=off should return SIGUSR2, rv %d errno %d", rv, errno); ATF_REQUIRE(waitid(P_PID, pid, NULL, WEXITED) == 0); } static void support_check_siginfo(int code, int status, pid_t pid, siginfo_t *si, int sig) { ATF_REQUIRE_EQ_MSG(sig, si->si_signo, "check_siginfo: si_signo %d != sig %d", si->si_signo, sig); ATF_REQUIRE_EQ_MSG(code, si->si_code, "check_siginfo: si_code %d != code %d", si->si_code, code); ATF_REQUIRE_EQ_MSG(status, si->si_status, "check_siginfo: si_status %d != status %d", si->si_status, status); ATF_REQUIRE_EQ_MSG(pid, si->si_pid, "check_siginfo: si_pid %d != pid %d", si->si_pid, pid); } static void support_check_sigchld(sigset_t *set, int code, int status, pid_t pid, bool dequeue) { siginfo_t si; int sig, kpid; if (dequeue == true) kpid = support_create_sig_proc(SIGUSR2, 1, 1000000); sig = sigwaitinfo(set, &si); if (dequeue == true) { ATF_REQUIRE_EQ_MSG(-1, sig, "sigwaitinfo() should fail, sig %d != -1", sig); ATF_REQUIRE_EQ_MSG(EINTR, errno, "sigwaitinfo() should fail errno %d != EINTR", errno); } else ATF_REQUIRE_EQ_MSG(SIGCHLD, sig, "sigwaitinfo() %d != SIGCHLD", sig); if (dequeue == false) support_check_siginfo(code, status, pid, &si, SIGCHLD); if (dequeue == true) ATF_REQUIRE(waitid(P_PID, kpid, &si, WEXITED) == 0); } static void test_child(void) { raise(SIGSTOP); while (1) pause(); } /* * Test kern.wait_dequeue_sigchld knob. */ static void test_wait_dequeue_sigchld(bool dequeue) { struct sigaction sa; siginfo_t si; sigset_t set; pid_t pid; sa.sa_flags = SA_SIGINFO | SA_RESTART; sa.sa_sigaction = dummy_sigchld; sigemptyset(&sa.sa_mask); ATF_REQUIRE(sigaction(SIGCHLD, &sa, NULL) == 0); support_signal(SIGUSR2, dummy_sig_handler); sigemptyset(&set); sigaddset(&set, SIGCHLD); ATF_REQUIRE(sigprocmask(SIG_BLOCK, &set, NULL) == 0); if (dequeue) support_sysctlset("kern.wait_dequeue_sigchld", 1); else support_sysctlset("kern.wait_dequeue_sigchld", 0); pid = fork(); ATF_REQUIRE(pid >= 0); if (pid == 0) { test_child(); exit(0); } bzero(&si, sizeof(si)); ATF_REQUIRE(waitid(P_PID, pid, &si, WSTOPPED) == 0); support_check_siginfo(CLD_STOPPED, SIGSTOP, pid, &si, SIGCHLD); support_check_sigchld(&set, CLD_STOPPED, SIGSTOP, pid, dequeue); ATF_REQUIRE(kill(pid, SIGCONT) == 0); bzero(&si, sizeof(si)); ATF_REQUIRE(waitid(P_PID, pid, &si, WCONTINUED) == 0); support_check_siginfo(CLD_CONTINUED, SIGCONT, pid, &si, SIGCHLD); support_check_sigchld(&set, CLD_CONTINUED, SIGCONT, pid, dequeue); ATF_REQUIRE(kill(pid, SIGKILL) == 0); bzero(&si, sizeof(si)); ATF_REQUIRE(waitid(P_PID, pid, &si, WEXITED) == 0); support_check_siginfo(CLD_KILLED, SIGKILL, pid, &si, SIGCHLD); } ATF_TC_WITH_CLEANUP(test_sig_discard_ign_true); ATF_TC_HEAD(test_sig_discard_ign_true, tc) { atf_tc_set_md_var(tc, "require.user", "root"); atf_tc_set_md_var(tc, "descr", "Test kern.sig_discard_ign on"); } ATF_TC_BODY(test_sig_discard_ign_true, tc) { test_sig_discard_ign(true); } ATF_TC_CLEANUP(test_sig_discard_ign_true, tc) { support_sysctlset("kern.sig_discard_ign", 1); } ATF_TC_WITH_CLEANUP(test_sig_discard_ign_false); ATF_TC_HEAD(test_sig_discard_ign_false, tc) { atf_tc_set_md_var(tc, "require.user", "root"); atf_tc_set_md_var(tc, "descr", "Test kern.sig_discard_ign off"); } ATF_TC_BODY(test_sig_discard_ign_false, tc) { test_sig_discard_ign(false); } ATF_TC_CLEANUP(test_sig_discard_ign_false, tc) { support_sysctlset("kern.sig_discard_ign", 1); } ATF_TC_WITH_CLEANUP(test_wait_dequeue_sigchld_true); ATF_TC_HEAD(test_wait_dequeue_sigchld_true, tc) { atf_tc_set_md_var(tc, "require.user", "root"); atf_tc_set_md_var(tc, "descr", "Test kern.wait_dequeue_sigchld on"); } ATF_TC_BODY(test_wait_dequeue_sigchld_true, tc) { test_wait_dequeue_sigchld(true); } ATF_TC_CLEANUP(test_wait_dequeue_sigchld_true, tc) { support_sysctlset("kern.wait_dequeue_sigchld", 1); } ATF_TC_WITH_CLEANUP(test_wait_dequeue_sigchld_false); ATF_TC_HEAD(test_wait_dequeue_sigchld_false, tc) { atf_tc_set_md_var(tc, "require.user", "root"); atf_tc_set_md_var(tc, "descr", "Test kern.wait_dequeue_sigchld off"); } ATF_TC_BODY(test_wait_dequeue_sigchld_false, tc) { test_wait_dequeue_sigchld(false); } ATF_TC_CLEANUP(test_wait_dequeue_sigchld_false, tc) { support_sysctlset("kern.wait_dequeue_sigchld", 1); } ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, test_sigtimedwait_timeout_eagain0); ATF_TP_ADD_TC(tp, test_sigtimedwait_timeout_eagain1); ATF_TP_ADD_TC(tp, test_sigtimedwait_timeout_eagain2); ATF_TP_ADD_TC(tp, test_sigtimedwait_timeout_eagain3); ATF_TP_ADD_TC(tp, test_sigtimedwait_large_timeout_eintr); ATF_TP_ADD_TC(tp, test_sigtimedwait_infinity); ATF_TP_ADD_TC(tp, test_sigtimedwait_einval); ATF_TP_ADD_TC(tp, test_sigwait_eintr); ATF_TP_ADD_TC(tp, test_sigwaitinfo_eintr); ATF_TP_ADD_TC(tp, test_sig_discard_ign_true); ATF_TP_ADD_TC(tp, test_sig_discard_ign_false); ATF_TP_ADD_TC(tp, test_wait_dequeue_sigchld_true); ATF_TP_ADD_TC(tp, test_wait_dequeue_sigchld_false); return (atf_no_error()); }