mirror of
https://github.com/checkpoint-restore/criu.git
synced 2026-07-18 00:58:31 +00:00
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 <ptikhomirov@virtuozzo.com> Signed-off-by: Andrei Vagin <avagin@virtuozzo.com>
This commit is contained in:
parent
3b33e16f3e
commit
4d9bf608b5
3 changed files with 118 additions and 0 deletions
|
|
@ -21,6 +21,7 @@ TST_NOFILE = \
|
|||
socket-tcp6 \
|
||||
shmem \
|
||||
lazy-thp \
|
||||
pid_reuse \
|
||||
|
||||
|
||||
TST_FILE = \
|
||||
|
|
|
|||
116
test/zdtm/transition/pid_reuse.c
Normal file
116
test/zdtm/transition/pid_reuse.c
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
#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;
|
||||
}
|
||||
1
test/zdtm/transition/pid_reuse.desc
Normal file
1
test/zdtm/transition/pid_reuse.desc
Normal file
|
|
@ -0,0 +1 @@
|
|||
{'flags': 'suid pre-dump-notify'}
|
||||
Loading…
Add table
Add a link
Reference in a new issue