Commit 4d9bf608 authored by Pavel Tikhomirov's avatar Pavel Tikhomirov Committed by Pavel Emelyanov

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: 's avatarPavel Tikhomirov <ptikhomirov@virtuozzo.com>
Signed-off-by: 's avatarAndrei Vagin <avagin@virtuozzo.com>
parent 3b33e16f
...@@ -21,6 +21,7 @@ TST_NOFILE = \ ...@@ -21,6 +21,7 @@ TST_NOFILE = \
socket-tcp6 \ socket-tcp6 \
shmem \ shmem \
lazy-thp \ lazy-thp \
pid_reuse \
TST_FILE = \ TST_FILE = \
......
#include <fcntl.h>
#include <sys/wait.h>
#include <sys/mman.h>
#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 <ptikhomirov@virtuozzo.com>";
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;
}
{'flags': 'suid pre-dump-notify'}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment