Skip to content

Commit 9c56092

Browse files
committed
test/zdtm/static: add maps11 test for MAP_DROPPABLE/MADV_WIPEONFORK
In this test we want to ensure that contents of droppable mappings and mappings with MADV_WIPEONFORK is properly restored in parent/child processes. Signed-off-by: Alexander Mikhalitsyn <aleksandr.mikhalitsyn@canonical.com>
1 parent 99a9506 commit 9c56092

File tree

2 files changed

+179
-0
lines changed

2 files changed

+179
-0
lines changed

test/zdtm/static/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ TST_NOFILE := \
150150
maps05 \
151151
maps09 \
152152
maps10 \
153+
maps11 \
153154
mlock_setuid \
154155
xids00 \
155156
groups \

test/zdtm/static/maps11.c

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
#include <stdint.h>
2+
#include <signal.h>
3+
#include <sys/mman.h>
4+
#include <sys/stat.h>
5+
#include <sys/types.h>
6+
#include <sys/wait.h>
7+
#include "zdtmtst.h"
8+
9+
#ifndef MAP_DROPPABLE
10+
#define MAP_DROPPABLE 0x08
11+
#endif
12+
13+
#ifndef MADV_WIPEONFORK
14+
#define MADV_WIPEONFORK 18
15+
#endif
16+
17+
const char *test_doc = "Test MAP_DROPPABLE/MADV_WIPEONFORK mappings with 2 processes";
18+
const char *test_author = "Alexander Mikhalitsyn <aleksandr.mikhalitsyn@canonical.com>";
19+
20+
bool mem_is_zero(const uint8_t *buffer, size_t length)
21+
{
22+
size_t i;
23+
24+
for (i = 0; i < length; i++)
25+
if (buffer[i] != 0)
26+
return false;
27+
28+
return true;
29+
}
30+
31+
int main(int argc, char **argv)
32+
{
33+
uint8_t *p1, *p2;
34+
pid_t pid;
35+
int status;
36+
const char data[] = "MADV_WIPEONFORK vma data";
37+
bool criu_was_there = false;
38+
struct stat st1, st2;
39+
40+
test_init(argc, argv);
41+
42+
p1 = mmap(NULL, sizeof(data), PROT_READ | PROT_WRITE, MAP_DROPPABLE | MAP_ANONYMOUS, 0, 0);
43+
if (p1 == MAP_FAILED) {
44+
if (errno == EINVAL) {
45+
skip("mmap failed, no kernel support for MAP_DROPPABLE\n");
46+
goto skip;
47+
} else {
48+
pr_perror("mmap failed");
49+
return -1;
50+
}
51+
}
52+
53+
p2 = mmap(NULL, sizeof(data), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
54+
if (p2 == MAP_FAILED) {
55+
pr_perror("mmap failed");
56+
return 1;
57+
}
58+
59+
if (madvise(p2, sizeof(data), MADV_WIPEONFORK)) {
60+
pr_perror("madvise failed");
61+
return -1;
62+
}
63+
64+
/* contents of this mapping is supposed to be dropped after C/R */
65+
memcpy(p1, data, sizeof(data));
66+
67+
/* contents of this mapping is supposed to be dropped after fork() */
68+
memcpy(p2, data, sizeof(data));
69+
70+
/*
71+
* Let's spawn a process before C/R so our mappings get inherited
72+
* then, after C/R we need to ensure that CRIU memory premapping
73+
* machinery works properly.
74+
*
75+
* It is important, because we restore MADV_WIPEONFORK on a later
76+
* stages (after vma premapping happens) and we need to ensure that CRIU
77+
* handles everything in a right way.
78+
*/
79+
pid = test_fork();
80+
if (pid < 0) {
81+
pr_perror("fork failed");
82+
return 1;
83+
}
84+
85+
if (pid == 0) {
86+
test_waitsig();
87+
88+
/*
89+
* Both mappings have VM_WIPEONFORK flag set,
90+
* so we expect to have it null-ified after fork().
91+
*/
92+
if (!mem_is_zero(p1, sizeof(data)) || !mem_is_zero(p2, sizeof(data)))
93+
return 1;
94+
95+
return 0;
96+
}
97+
98+
/*
99+
* A simple way to detect if C/R happend is to compare st_ino
100+
* fields of stat() on the procfs files of the current task.
101+
*
102+
* Hopefully, this terrible hack is never used in real-world applications ;-)
103+
* Here, we only need this to make test to work with/without --nocr option.
104+
*/
105+
if (stat("/proc/self/status", &st1)) {
106+
pr_perror("stat");
107+
return 1;
108+
}
109+
110+
test_daemon();
111+
test_waitsig();
112+
113+
kill(pid, SIGTERM);
114+
waitpid(pid, &status, 0);
115+
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
116+
goto err;
117+
118+
if (stat("/proc/self/status", &st2)) {
119+
pr_perror("stat");
120+
return 1;
121+
}
122+
123+
/* detect CRIU */
124+
criu_was_there = st1.st_ino != st2.st_ino;
125+
126+
/*
127+
* We should mark failure if one of the following happens:
128+
* 1. MAP_DROPPABLE memory is not zero after C/R
129+
* 2. MAP_DROPPABLE memory somehow changed without C/R (kernel issue? memory pressure?)
130+
* 3. MADV_WIPEONFORK memory is not preserved
131+
*
132+
* We care about 2nd case only because we would like test to pass even with --nocr
133+
* zdtm.py option.
134+
*/
135+
if ((criu_was_there && !mem_is_zero(p1, sizeof(data))) ||
136+
(!criu_was_there && memcmp(p1, data, sizeof(data))) ||
137+
memcmp(p2, data, sizeof(data))) {
138+
fail("Data mismatch");
139+
return 1;
140+
}
141+
142+
/* contents of these mappings is supposed to be dropped after fork() */
143+
memcpy(p1, data, sizeof(data));
144+
memcpy(p2, data, sizeof(data));
145+
146+
pid = test_fork();
147+
if (pid < 0) {
148+
pr_perror("fork failed");
149+
return 1;
150+
}
151+
152+
if (pid == 0) {
153+
if (!mem_is_zero(p1, sizeof(data)) || !mem_is_zero(p2, sizeof(data)))
154+
return 1;
155+
156+
return 0;
157+
}
158+
159+
waitpid(pid, &status, 0);
160+
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
161+
goto err;
162+
163+
pass();
164+
165+
return 0;
166+
err:
167+
if (waitpid(-1, NULL, WNOHANG) == 0) {
168+
kill(pid, SIGTERM);
169+
wait(NULL);
170+
}
171+
return 1;
172+
173+
skip:
174+
test_daemon();
175+
test_waitsig();
176+
pass();
177+
return 0;
178+
}

0 commit comments

Comments
 (0)