Skip to content

Commit 4dedbae

Browse files
committed
test/zdtm/static/maps12: add madv guards test
Test for madvise(MADV_GUARD_INSTALL). Signed-off-by: Alexander Mikhalitsyn <[email protected]>
1 parent e4f20d7 commit 4dedbae

File tree

3 files changed

+352
-0
lines changed

3 files changed

+352
-0
lines changed

test/zdtm/static/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,7 @@ TST_FILE = \
315315
write_read02 \
316316
write_read10 \
317317
maps00 \
318+
maps12 \
318319
link10 \
319320
file_attr \
320321
deleted_unix_sock \

test/zdtm/static/maps12.c

Lines changed: 350 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,350 @@
1+
#include <errno.h>
2+
#include <fcntl.h>
3+
#include <unistd.h>
4+
#include <setjmp.h>
5+
#include <stdlib.h>
6+
#include <signal.h>
7+
#include <sys/mman.h>
8+
#include <sys/types.h>
9+
#include <sys/wait.h>
10+
#include <linux/limits.h>
11+
#include "zdtmtst.h"
12+
13+
const char *test_doc = "Test madvise(MADV_GUARD_INSTALL)";
14+
const char *test_author = "Alexander Mikhalitsyn <[email protected]>";
15+
/* some parts of code were taken from Linux kernel's kselftest guard-pages.c
16+
written by Lorenzo Stoakes <[email protected]> */
17+
18+
char *filename;
19+
int fd;
20+
TEST_OPTION(filename, string, "file name", 1);
21+
22+
#ifndef MADV_GUARD_INSTALL
23+
#define MADV_GUARD_INSTALL 102
24+
#endif
25+
26+
uint8_t *map_base;
27+
28+
struct {
29+
unsigned int pages_num;
30+
bool filemap;
31+
} vmas[] = {
32+
{ 2, false },
33+
{ 2, false },
34+
{ 2, false },
35+
{ 2, true },
36+
{ 2, true },
37+
{ 2, true },
38+
};
39+
40+
struct {
41+
bool guarded;
42+
bool wipeonfork;
43+
} pages[] = {
44+
{ false, false }, /* vmas[0] */
45+
{ true, false },
46+
{ true, false }, /* vmas[1] */
47+
{ false, false },
48+
{ false, false }, /* vmas[2] */
49+
{ true, true },
50+
{ true, false }, /* vmas[3] */
51+
{ false, false },
52+
{ true, false }, /* vmas[4] */
53+
{ true, false },
54+
{ false, false }, /* vmas[5] */
55+
{ true, false },
56+
};
57+
58+
static volatile sig_atomic_t signal_jump_set;
59+
static sigjmp_buf signal_jmp_buf;
60+
61+
static void handle_sigsegv(int signo)
62+
{
63+
if (!signal_jump_set)
64+
return;
65+
66+
siglongjmp(signal_jmp_buf, 1);
67+
}
68+
69+
static bool try_write_to_addr(uint8_t *ptr)
70+
{
71+
bool failed;
72+
73+
/* Tell signal handler to jump back here on fatal signal. */
74+
signal_jump_set = true;
75+
/* If a fatal signal arose, we will jump back here and failed is set. */
76+
failed = sigsetjmp(signal_jmp_buf, 1) != 0;
77+
78+
if (!failed)
79+
*ptr = 'x';
80+
81+
signal_jump_set = false;
82+
return !failed;
83+
}
84+
85+
static int setup_sigsegv_handler(void)
86+
{
87+
uint8_t write_me;
88+
89+
if (signal(SIGSEGV, handle_sigsegv) == SIG_ERR) {
90+
pr_perror("setting SIGSEGV handler failed");
91+
return 1;
92+
}
93+
94+
/* ensure that try_write_to_addr() works properly */
95+
if (!try_write_to_addr(&write_me)) {
96+
pr_err("Failed to write at valid addr. Buggy try_write_to_addr()?\n");
97+
return 1;
98+
}
99+
100+
if (try_write_to_addr(NULL)) {
101+
pr_err("Failed to detect an invalid write. Buggy try_write_to_addr()?\n");
102+
return 1;
103+
}
104+
105+
return 0;
106+
}
107+
108+
static inline void *mmap_pages(void *addr_hint, unsigned int count, bool filemap)
109+
{
110+
char *map;
111+
112+
map = mmap(addr_hint, count * PAGE_SIZE, PROT_WRITE | PROT_READ,
113+
MAP_PRIVATE | (filemap ? 0 : MAP_ANONYMOUS) | (addr_hint ? MAP_FIXED : 0),
114+
filemap ? fd : -1, filemap ? ((off_t)addr_hint - (off_t)map_base) : 0);
115+
if (map == MAP_FAILED || (addr_hint && (map != addr_hint)))
116+
return MAP_FAILED;
117+
118+
return map;
119+
}
120+
121+
static int __check_guards(const char *when, bool in_child)
122+
{
123+
int i;
124+
125+
for (i = 0; i < ARRAY_SIZE(pages); i++) {
126+
/*
127+
* Skip pages that were never guarded, and also those
128+
* that were, but have MADV_WIPEONFORK which means that
129+
* guards were removed on fork.
130+
*/
131+
if (!pages[i].guarded || (in_child && pages[i].wipeonfork))
132+
continue;
133+
134+
if (try_write_to_addr(&map_base[i * PAGE_SIZE])) {
135+
pr_err("successful write to a guarded area %d %s C/R\n",
136+
i, when);
137+
return 1;
138+
}
139+
}
140+
141+
return 0;
142+
}
143+
144+
static int check_guards(const char *when)
145+
{
146+
int status;
147+
pid_t pid;
148+
149+
/*
150+
* First of all, check that guards are on their places
151+
* in a main test process.
152+
*/
153+
if (__check_guards(when, false)) {
154+
return 1;
155+
}
156+
157+
/*
158+
* Now, check that guards are on their places
159+
* after fork(). This allows to ensure that
160+
* combo MADV_WIPEONFORK + MADV_GUARD_INSTALL
161+
* is restored properly too.
162+
*/
163+
164+
pid = test_fork();
165+
if (pid < 0) {
166+
pr_perror("check_guards: fork failed");
167+
return 1;
168+
}
169+
170+
if (pid == 0) {
171+
if (__check_guards(when, true)) {
172+
pr_err("check_guards(\"%s\") failed in child\n", when);
173+
exit(1);
174+
}
175+
176+
exit(0);
177+
}
178+
179+
if (waitpid(pid, &status, 0) != pid) {
180+
pr_perror("check_guards: waitpid");
181+
return 1;
182+
}
183+
184+
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
185+
pr_err("check_guards: process didn't exit cleanly: status=%d\n", status);
186+
return 1;
187+
}
188+
189+
return 0;
190+
}
191+
192+
static void gen_pages_data(void)
193+
{
194+
int i;
195+
196+
for (i = 0; i < ARRAY_SIZE(pages); i++) {
197+
uint32_t crc;
198+
199+
if (pages[i].guarded)
200+
continue;
201+
202+
crc = ~0;
203+
datagen(&map_base[i * PAGE_SIZE], PAGE_SIZE, &crc);
204+
}
205+
}
206+
207+
static int set_pages_madvs(void)
208+
{
209+
int i;
210+
211+
for (i = 0; i < ARRAY_SIZE(pages); i++) {
212+
if (pages[i].guarded) {
213+
if (madvise(&map_base[i * PAGE_SIZE], PAGE_SIZE,
214+
MADV_GUARD_INSTALL)) {
215+
pr_perror("MADV_GUARD_INSTALL failed on page %d", i);
216+
return 1;
217+
}
218+
}
219+
220+
if (pages[i].wipeonfork) {
221+
if (madvise(&map_base[i * PAGE_SIZE], PAGE_SIZE,
222+
MADV_WIPEONFORK)) {
223+
pr_perror("MADV_WIPEONFORK failed on page %d", i);
224+
return 1;
225+
}
226+
}
227+
}
228+
229+
return 0;
230+
}
231+
232+
static int check_pages_data(void)
233+
{
234+
int i;
235+
236+
for (i = 0; i < ARRAY_SIZE(pages); i++) {
237+
uint32_t crc;
238+
239+
if (pages[i].guarded)
240+
continue;
241+
242+
crc = ~0;
243+
if (datachk(&map_base[i * PAGE_SIZE], PAGE_SIZE, &crc)) {
244+
pr_err("Page %d is corrupted\n", i);
245+
return 1;
246+
}
247+
}
248+
249+
return 0;
250+
}
251+
252+
static int prepare_vmas(void)
253+
{
254+
char *map;
255+
int i, shift;
256+
257+
shift = 0;
258+
for (i = 0; i < ARRAY_SIZE(vmas); i++) {
259+
map = mmap_pages(&map_base[shift * PAGE_SIZE],
260+
vmas[i].pages_num, vmas[i].filemap);
261+
if (map == MAP_FAILED) {
262+
pr_err("mmap of [%d,%d] pages failed\n",
263+
shift, shift + vmas[i].pages_num);
264+
return 1;
265+
}
266+
267+
shift += vmas[i].pages_num;
268+
}
269+
270+
if (shift != ARRAY_SIZE(pages)) {
271+
pr_err("Different number of pages in vmas and pages arrays.\n");
272+
return 1;
273+
}
274+
275+
return 0;
276+
}
277+
278+
int main(int argc, char **argv)
279+
{
280+
unsigned int pages_num = ARRAY_SIZE(pages);
281+
282+
test_init(argc, argv);
283+
284+
fd = open(filename, O_TRUNC | O_CREAT | O_RDWR, 0600);
285+
if (fd < 0) {
286+
pr_perror("Unable to create a test file");
287+
return -1;
288+
}
289+
290+
if (ftruncate(fd, pages_num * PAGE_SIZE)) {
291+
pr_perror("Unable to ftruncate a test file");
292+
return -1;
293+
}
294+
295+
if (setup_sigsegv_handler()) {
296+
pr_err("setup_sigsegv_handler() failed\n");
297+
return 1;
298+
}
299+
300+
/* let's find a large enough area in address space */
301+
map_base = mmap_pages(NULL, pages_num, false);
302+
if (map_base == MAP_FAILED) {
303+
pr_err("mmap of %d pages failed\n", pages_num);
304+
return 1;
305+
}
306+
307+
/*
308+
* Now we know that we have a free vm address space area
309+
* [map_base, map_base + pages_num * PAGE_SIZE).
310+
* We can use (map_base) as a hint for our further mmaps.
311+
*/
312+
if (prepare_vmas()) {
313+
pr_err("prepare_vmas() failed\n");
314+
return 1;
315+
}
316+
317+
/* fill non-guarded pages with data and preserve checksums */
318+
gen_pages_data();
319+
320+
if (set_pages_madvs()) {
321+
pr_err("set_pages_madvs() failed\n");
322+
return 1;
323+
}
324+
325+
/* ensure that madvise(MADV_GUARD_INSTALL) works like expected */
326+
if (check_guards("before")) {
327+
pr_err("check_guards(\"before\") failed\n");
328+
return 1;
329+
}
330+
331+
test_daemon();
332+
test_waitsig();
333+
334+
/* ensure that guards are at their places */
335+
if (check_guards("after")) {
336+
fail("check_guards(\"after\") failed");
337+
return 1;
338+
}
339+
340+
/* check that non-guarded pages still contain original data */
341+
if (check_pages_data()) {
342+
fail("check_pages_data() failed");
343+
return 1;
344+
}
345+
346+
pass();
347+
munmap(map_base, pages_num * PAGE_SIZE);
348+
close(fd);
349+
return 0;
350+
}

test/zdtm/static/maps12.desc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{'flavor': 'h', 'feature': 'pagemap_scan_guard_pages'}

0 commit comments

Comments
 (0)