From 4d9bf608b59b6e323f346b0beb956b02ecbef294 Mon Sep 17 00:00:00 2001 From: Pavel Tikhomirov Date: Fri, 6 Apr 2018 10:31:32 +0300 Subject: [PATCH] zdtm: check that pid-reuse does not break iterative memory dump The idea of the test is: 1) mmap separate page and put variable there, so that other usage does not dirty these region. Initialize the variable with VALUE_A. 2) fork a child with special pid == CHILD_NS_PID. Only if it is a first child overwrite the variable with VALUE_B. 3) wait for the end of the next predump or end of restore with test_wait_pre_dump_ack/test_wait_pre_dump pair and kill our child. Note: The memory region is "clean" in parent. 4) goto (2) unles end of cr is reported by test_waitpre So on first iteration child with pid CHILD_NS_PID was dumped with VALUE_B, on all other iterations and on final dump other child with the same pid exists but with VALUE_A. But on all iterations after the first one we have these memory region "clean". So criu before the fix would have restored the VALUE_B taking it from first child's image, but should restore VALUE_A. Note: Child in its turn waits termination and performs a check that variable value doesn't change after c/r. We should run the test with at least one predump to trigger the problem: [root@snorch criu]# ./test/zdtm.py run --pre 1 -k always -t zdtm/transition/pid_reuse Checking feature ns_pid Checking feature ns_get_userns Checking feature ns_get_parent === Run 1/1 ================ zdtm/transition/pid_reuse ===================== Run zdtm/transition/pid_reuse in ns ====================== DEP pid_reuse.d CC pid_reuse.o LINK pid_reuse Start test Test is SUID ./pid_reuse --pidfile=pid_reuse.pid --outfile=pid_reuse.out Run criu pre-dump Send the 10 signal to 52 Run criu dump Run criu restore Send the 15 signal to 73 Wait for zdtm/transition/pid_reuse(73) to die for 0.100000 Test output: ================================ 14:47:57.717: 11235: ERR: pid_reuse.c:76: Wrong value in a variable after restore 14:47:57.717: 4: FAIL: pid_reuse.c:110: Task 11235 exited with wrong code 1 (errno = 11 (Resource temporarily unavailable)) <<< ================================ https://jira.sw.ru/browse/PSBM-67502 v3: simplify waitpid's status check v9: switch to test_wait_pre_dump(_ack) Signed-off-by: Pavel Tikhomirov Signed-off-by: Andrei Vagin --- test/zdtm/transition/Makefile | 1 + test/zdtm/transition/pid_reuse.c | 116 ++++++++++++++++++++++++++++ test/zdtm/transition/pid_reuse.desc | 1 + 3 files changed, 118 insertions(+) create mode 100644 test/zdtm/transition/pid_reuse.c create mode 100644 test/zdtm/transition/pid_reuse.desc diff --git a/test/zdtm/transition/Makefile b/test/zdtm/transition/Makefile index f482a8a09..35301ac85 100644 --- a/test/zdtm/transition/Makefile +++ b/test/zdtm/transition/Makefile @@ -21,6 +21,7 @@ TST_NOFILE = \ socket-tcp6 \ shmem \ lazy-thp \ + pid_reuse \ TST_FILE = \ diff --git a/test/zdtm/transition/pid_reuse.c b/test/zdtm/transition/pid_reuse.c new file mode 100644 index 000000000..f8f923069 --- /dev/null +++ b/test/zdtm/transition/pid_reuse.c @@ -0,0 +1,116 @@ +#include +#include +#include + +#include "zdtmtst.h" + +const char *test_doc = "Tests that forking tasks with same pid does not break iterative dump"; +const char *test_author = "Pavel Tikhomirov "; + +enum { + VALUE_A = 1, + VALUE_B = 2, +}; + +#define CHILD_NS_PID 11235 + +static int set_ns_next_pid(pid_t pid) +{ + char buf[32]; + int len, fd; + + fd = open("/proc/sys/kernel/ns_last_pid", O_WRONLY); + if (fd < 0) { + pr_perror("Failed to open ns_last_pid"); + return -1; + } + + len = snprintf(buf, sizeof(buf), "%d", pid - 1); + len -= write(fd, buf, len); + if (len) + pr_perror("Can't set ns_last_pid"); + close(fd); + + return len ? -1 : 0; +} + +int main(int argc, char **argv) +{ + int pid, wpid, status; + bool overwrite = true; + bool wait = true; + int *variable; + void *mem; + + test_init(argc, argv); + + mem = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + if (mem == MAP_FAILED) { + pr_perror("Can't mmap memory region"); + return 1; + } + + variable = (int *)mem; + *variable = VALUE_A; + + test_daemon(); + + while (wait) { + if (set_ns_next_pid(CHILD_NS_PID)) + return 1; + + pid = fork(); + if (pid == -1) { + pr_perror("fork"); + return 1; + } else if (pid == 0) { + if (overwrite) + *variable = VALUE_B; + + /* Reuse test_waitsig to wait SIGTERM from parent */ + test_waitsig(); + + if (*variable != (overwrite ? VALUE_B : VALUE_A)) { + pr_err("Wrong value in a variable after restore\n"); + exit(1); + } + exit(0); + } + + if (pid != CHILD_NS_PID) { + pr_err("Child started with wrong pid %d (expected %d)\n", pid, CHILD_NS_PID); + kill(pid, SIGKILL); + waitpid(pid, NULL, 0); + return 1; + } + + /* Notify we are ready for a next pre-dump/dump */ + if (!overwrite) + test_wait_pre_dump_ack(); + + /* Wait for next pre-dump/dump finish */ + if (test_wait_pre_dump()) + wait = false; + + if (kill(pid, SIGTERM)) { + pr_perror("kill"); + return 1; + } + + wpid = waitpid(pid, &status, 0); + if (wpid <= 0) { + pr_perror("waitpid"); + return 1; + } + + if (status) { + fail("Task %d died with exit status %x", wpid, status); + return 1; + } + + overwrite = false; + } + + pass(); + return 0; +} diff --git a/test/zdtm/transition/pid_reuse.desc b/test/zdtm/transition/pid_reuse.desc new file mode 100644 index 000000000..474f44ebf --- /dev/null +++ b/test/zdtm/transition/pid_reuse.desc @@ -0,0 +1 @@ +{'flags': 'suid pre-dump-notify'}