Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src Add new ATF tests: kernel/t_zombie



details:   https://anonhg.NetBSD.org/src/rev/1776947f96d2
branches:  trunk
changeset: 321382:1776947f96d2
user:      kamil <kamil%NetBSD.org@localhost>
date:      Wed Mar 14 02:13:47 2018 +0000

description:
Add new ATF tests: kernel/t_zombie

New tests attempting to kill, stop, drop or revive a zombie:
 - signal1 (SIGKILL)
 - signal2 (SIGSTOP)
 - signal3 (SIGABRT)
 - signal4 (SIGHUP)
 - signal5 (SIGCONT)

New test race1 verifying whether there are any kernel races when processing
signals to zombies, executing in a loop for 5 seconds.

These tests were inspired by a kernel unexpected behavior when a lookup
of a dying process could result in two detected entities once as an alive
process and once as a zombie.

race1 is similar to t_ptrace_wait* race1, however without ptrace(2) involved.

Sponsored by <The NetBSD Foundation>

diffstat:

 distrib/sets/lists/debug/mi |    3 +-
 distrib/sets/lists/tests/mi |    3 +-
 tests/kernel/Makefile       |    3 +-
 tests/kernel/t_zombie.c     |  276 ++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 282 insertions(+), 3 deletions(-)

diffs (truncated from 331 to 300 lines):

diff -r 735ed7ae8c3d -r 1776947f96d2 distrib/sets/lists/debug/mi
--- a/distrib/sets/lists/debug/mi       Tue Mar 13 23:03:21 2018 +0000
+++ b/distrib/sets/lists/debug/mi       Wed Mar 14 02:13:47 2018 +0000
@@ -1,4 +1,4 @@
-# $NetBSD: mi,v 1.244 2018/03/09 20:20:48 joerg Exp $
+# $NetBSD: mi,v 1.245 2018/03/14 02:13:47 kamil Exp $
 ./etc/mtree/set.debug                           comp-sys-root
 ./usr/lib                                      comp-sys-usr            compatdir
 ./usr/lib/i18n/libBIG5_g.a                     comp-c-debuglib         debuglib,compatfile
@@ -1750,6 +1750,7 @@
 ./usr/libdata/debug/usr/tests/kernel/t_timeleft.debug                  tests-kernel-tests      debug,atf,compattestfile
 ./usr/libdata/debug/usr/tests/kernel/t_ucontext.debug                  tests-obsolete          obsolete,compattestfile
 ./usr/libdata/debug/usr/tests/kernel/t_writev.debug                    tests-obsolete          obsolete,compattestfile
+./usr/libdata/debug/usr/tests/kernel/t_zombie.debug                    tests-kernel-tests      debug,atf,rump
 ./usr/libdata/debug/usr/tests/kernel/tty/t_pr.debug                    tests-kernel-tests      debug,atf,rump
 ./usr/libdata/debug/usr/tests/kyua-cli/bootstrap/atf_helpers.debug                     tests-kyua-tests        debug,atf,kyua,compattestfile
 ./usr/libdata/debug/usr/tests/kyua-cli/bootstrap/plain_helpers.debug                   tests-kyua-tests        debug,atf,kyua,compattestfile
diff -r 735ed7ae8c3d -r 1776947f96d2 distrib/sets/lists/tests/mi
--- a/distrib/sets/lists/tests/mi       Tue Mar 13 23:03:21 2018 +0000
+++ b/distrib/sets/lists/tests/mi       Wed Mar 14 02:13:47 2018 +0000
@@ -1,4 +1,4 @@
-# $NetBSD: mi,v 1.776 2018/03/09 20:20:48 joerg Exp $
+# $NetBSD: mi,v 1.777 2018/03/14 02:13:47 kamil Exp $
 #
 # Note: don't delete entries from here - mark them as "obsolete" instead.
 #
@@ -2197,6 +2197,7 @@
 ./usr/tests/kernel/t_umount                    tests-kernel-tests      compattestfile,atf
 ./usr/tests/kernel/t_umountstress              tests-kernel-tests      compattestfile,atf
 ./usr/tests/kernel/t_writev                    tests-obsolete          obsolete
+./usr/tests/kernel/t_zombie                    tests-kernel-tests      compattestfile,atf
 ./usr/tests/kernel/tty                         tests-kernel-tests      compattestfile,atf
 ./usr/tests/kernel/tty/Atffile                 tests-kernel-tests      atf,rump
 ./usr/tests/kernel/tty/Kyuafile                        tests-kernel-tests      atf,rump,kyua
diff -r 735ed7ae8c3d -r 1776947f96d2 tests/kernel/Makefile
--- a/tests/kernel/Makefile     Tue Mar 13 23:03:21 2018 +0000
+++ b/tests/kernel/Makefile     Wed Mar 14 02:13:47 2018 +0000
@@ -1,4 +1,4 @@
-# $NetBSD: Makefile,v 1.52 2017/12/14 14:38:17 nakayama Exp $
+# $NetBSD: Makefile,v 1.53 2018/03/14 02:13:47 kamil Exp $
 
 NOMAN=         # defined
 
@@ -16,6 +16,7 @@
 TESTS_C+=      t_kauth_pr_47598
 TESTS_C+=      t_sysctl
 TESTS_C+=      t_timeleft
+TESTS_C+=      t_zombie
 
 TESTS_SH=      t_umount
 TESTS_SH+=     t_umountstress
diff -r 735ed7ae8c3d -r 1776947f96d2 tests/kernel/t_zombie.c
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/kernel/t_zombie.c   Wed Mar 14 02:13:47 2018 +0000
@@ -0,0 +1,276 @@
+/*     $NetBSD: t_zombie.c,v 1.1 2018/03/14 02:13:47 kamil Exp $       */
+
+/*-
+ * Copyright (c) 2018 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__COPYRIGHT("@(#) Copyright (c) 2018\
+ The NetBSD Foundation, inc. All rights reserved.");
+__RCSID("$NetBSD: t_zombie.c,v 1.1 2018/03/14 02:13:47 kamil Exp $");
+
+#include <sys/types.h>
+#include <sys/sysctl.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <time.h>
+#include <unistd.h>
+#include <err.h>
+
+#include <atf-c.h>
+
+static int debug = 0;
+
+#define DPRINTF(a, ...)                                                        \
+do {                                                                   \
+        if (debug) printf(a,  ##__VA_ARGS__);                          \
+} while (/*CONSTCOND*/0)
+
+/*
+ * A child process cannot call atf functions and expect them to magically
+ * work like in the parent.
+ * The printf(3) messaging from a child will not work out of the box as well
+ * without estabilishing a communication protocol with its parent. To not
+ * overcomplicate the tests - do not log from a child and use err(3)/errx(3)
+ * wrapped with ASSERT_EQ()/ASSERT_NEQ() as that is guaranteed to work.
+ */
+#define ASSERT_EQ(x, y)                                                                \
+do {                                                                           \
+       uintmax_t vx = (x);                                                     \
+       uintmax_t vy = (y);                                                     \
+       int ret = vx == vy;                                                     \
+       if (!ret)                                                               \
+               errx(EXIT_FAILURE, "%s:%d %s(): Assertion failed for: "         \
+                   "%s(%ju) == %s(%ju)", __FILE__, __LINE__, __func__,         \
+                   #x, vx, #y, vy);                                            \
+} while (/*CONSTCOND*/0)
+
+#define ASSERT_NEQ(x, y)                                                       \
+do {                                                                           \
+       uintmax_t vx = (x);                                                     \
+       uintmax_t vy = (y);                                                     \
+       int ret = vx != vy;                                                     \
+       if (!ret)                                                               \
+               errx(EXIT_FAILURE, "%s:%d %s(): Assertion failed for: "         \
+                   "%s(%ju) != %s(%ju)", __FILE__, __LINE__, __func__,         \
+                   #x, vx, #y, vy);                                            \
+} while (/*CONSTCOND*/0)
+
+#define ASSERT(x)                                                              \
+do {                                                                           \
+       int ret = (x);                                                          \
+       if (!ret)                                                               \
+               errx(EXIT_FAILURE, "%s:%d %s(): Assertion failed for: %s",      \
+                    __FILE__, __LINE__, __func__, #x);                         \
+} while (/*CONSTCOND*/0)
+
+static bool
+check_zombie(pid_t process)
+{
+       struct kinfo_proc2 p;
+       size_t len = sizeof(p);
+
+       const int name[] = {
+               [0] = CTL_KERN,
+               [1] = KERN_PROC2,
+               [2] = KERN_PROC_PID,
+               [3] = process,
+               [4] = sizeof(p),
+               [5] = 1
+       };
+
+       const size_t namelen = __arraycount(name);
+
+       ASSERT_EQ(sysctl(name, namelen, &p, &len, NULL, 0), 0);
+
+       return (p.p_stat == LSZOMB);
+}
+
+static void __used
+await_zombie(pid_t process)
+{
+
+       /* Await the process becoming a zombie */
+       while (!check_zombie(process)) {
+               ASSERT_EQ(usleep(100), 0);
+       }
+}
+
+static void
+signal_raw(int sig)
+{
+       int status;
+       pid_t child1, child2, pid;
+
+       child1 = atf_utils_fork();
+       ATF_REQUIRE(child1 != 1);
+       if (child1 == 0) {
+               /* Just die and turn into a zombie */
+               _exit(0);
+       }
+
+       child2 = atf_utils_fork();
+       ATF_REQUIRE(child2 != 1);
+       if (child2 == 0) {
+               await_zombie(child1);
+
+               /*
+                * zombie does not process signals
+                * POSIX requires that zombie does not set errno ESRCH
+                * return value of kill() for a zombie is not specified
+                *
+                * Try to emit a signal towards it from an unrelated process.
+                */
+               errno = 0;
+               kill(child1, sig);
+               ASSERT_NEQ(errno, ESRCH);
+
+               /* A zombie is still a zombie waiting for collecting */
+               ASSERT(check_zombie(child1));
+
+               _exit(0);
+       }
+
+       pid = waitpid(child2, &status, WEXITED);
+       ATF_REQUIRE_EQ(pid, child2);
+       ATF_REQUIRE(WIFEXITED(status));
+       ATF_REQUIRE(!WIFCONTINUED(status));
+       ATF_REQUIRE(!WIFSIGNALED(status));
+       ATF_REQUIRE(!WIFSTOPPED(status));
+       ATF_REQUIRE_EQ(WEXITSTATUS(status), 0);
+
+       /* Assert that child1 is still a zombie after collecting child2 */
+       ATF_REQUIRE(check_zombie(child1));
+
+       /*
+        * zombie does not process signals
+        * POSIX requires that zombie does not set errno ESRCH
+        * return value of kill() for a zombie is not specified
+        *
+        * Try to emit a signal towards it from the parent.
+        */
+       errno = 0;
+       kill(child1, sig);
+       // ATF_CHECK_NEQ not available
+       ASSERT_NEQ(errno, ESRCH);
+
+       /* Assert that child1 is still a zombie after emitting a signal */
+       ATF_REQUIRE(check_zombie(child1));
+
+       pid = waitpid(child1, &status, WEXITED);
+       ATF_REQUIRE_EQ(pid, child1);
+       ATF_REQUIRE(WIFEXITED(status));
+       ATF_REQUIRE(!WIFCONTINUED(status));
+       ATF_REQUIRE(!WIFSIGNALED(status));
+       ATF_REQUIRE(!WIFSTOPPED(status));
+       ATF_REQUIRE_EQ(WEXITSTATUS(status), 0);
+}
+
+#define KILLABLE(test, sig)                                                    \
+ATF_TC(test);                                                                  \
+ATF_TC_HEAD(test, tc)                                                          \
+{                                                                              \
+                                                                               \
+       atf_tc_set_md_var(tc, "descr",                                          \
+           "process is not killable with " #sig);                              \
+}                                                                              \
+                                                                               \
+ATF_TC_BODY(test, tc)                                                          \
+{                                                                              \
+                                                                               \
+       signal_raw(sig);                                                        \
+}
+
+KILLABLE(signal1, SIGKILL) /* non-maskable */
+KILLABLE(signal2, SIGSTOP) /* non-maskable */
+KILLABLE(signal3, SIGABRT) /* regular abort trap */
+KILLABLE(signal4, SIGHUP)  /* hangup */
+KILLABLE(signal5, SIGCONT) /* continued? */
+
+ATF_TC(race1);
+ATF_TC_HEAD(race1, tc)
+{
+
+       atf_tc_set_md_var(tc, "descr",
+           "check if there are any races with sending signals, killing and "
+           "lookup of a zombie");
+}
+
+ATF_TC_BODY(race1, tc)
+{
+        time_t start, end;
+        double diff;
+        unsigned long N = 0;
+       int sig;
+
+       /*
+        * Assert that a dying process can be correctly looked up
+        * with sysctl(3) kern.proc and operation KERN_PROC_PID.
+        *
+        * This test has been inspired by a bug fixed in
+        * sys/kern/kern_proc.c 1.211
+        * "Make sysctl_doeproc() more predictable"
+        */
+
+        start = time(NULL);
+        while (true) {
+               /*
+                * A signal number does not matter, but it does not harm to



Home | Main Index | Thread Index | Old Index