The branch main has been updated by dchagin:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=128b9bf96cae36c33b45a4d418ff502497bbb834

commit 128b9bf96cae36c33b45a4d418ff502497bbb834
Author:     Dmitry Chagin <[email protected]>
AuthorDate: 2022-04-25 10:20:12 +0000
Commit:     Dmitry Chagin <[email protected]>
CommitDate: 2022-04-25 10:20:12 +0000

    Add tests for sigwait family syscalls.
    
    MFC after:              2 weeks
---
 tests/sys/kern/Makefile  |   2 +
 tests/sys/kern/sigwait.c | 584 +++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 586 insertions(+)

diff --git a/tests/sys/kern/Makefile b/tests/sys/kern/Makefile
index 120567d32347..f4bf097f9420 100644
--- a/tests/sys/kern/Makefile
+++ b/tests/sys/kern/Makefile
@@ -27,6 +27,7 @@ ATF_TESTS_C+= ptrace_test
 TEST_METADATA.ptrace_test+=            timeout="15"
 ATF_TESTS_C+=  reaper
 ATF_TESTS_C+=  sigaltstack
+ATF_TESTS_C+=  sigwait
 .if ${MACHINE_ARCH} != "i386" && ${MACHINE_ARCH:Mpowerpc*} == ""
 ATF_TESTS_C+=  subr_physmem_test
 .endif
@@ -64,6 +65,7 @@ LIBADD.kcov+=                         pthread
 LIBADD.ktls_test+=                     crypto
 LIBADD.sendfile_helper+=               pthread
 LIBADD.fdgrowtable_test+=              util pthread kvm procstat
+LIBADD.sigwait+=                       rt
 
 NETBSD_ATF_TESTS_C+=   lockf_test
 NETBSD_ATF_TESTS_C+=   mqueue_test
diff --git a/tests/sys/kern/sigwait.c b/tests/sys/kern/sigwait.c
new file mode 100644
index 000000000000..0b6e078b6548
--- /dev/null
+++ b/tests/sys/kern/sigwait.c
@@ -0,0 +1,584 @@
+/*-
+ * Copyright (c) 2022 Dmitry Chagin <[email protected]>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/limits.h>
+#include <sys/time.h>
+#include <sys/sysctl.h>
+#include <sys/user.h>
+#include <sys/wait.h>
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+
+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);
+}
+
+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());
+}

Reply via email to