]>
Commit | Line | Data |
---|---|---|
b2441318 | 1 | // SPDX-License-Identifier: GPL-2.0 |
4f5ce5e8 DH |
2 | #define _GNU_SOURCE |
3 | #define __EXPORTED_HEADERS__ | |
4 | ||
5 | #include <errno.h> | |
6 | #include <inttypes.h> | |
7 | #include <limits.h> | |
8 | #include <linux/falloc.h> | |
9 | #include <linux/fcntl.h> | |
10 | #include <linux/memfd.h> | |
11 | #include <sched.h> | |
12 | #include <stdio.h> | |
13 | #include <stdlib.h> | |
14 | #include <signal.h> | |
15 | #include <string.h> | |
16 | #include <sys/mman.h> | |
17 | #include <sys/stat.h> | |
18 | #include <sys/syscall.h> | |
c1ee4831 | 19 | #include <sys/wait.h> |
4f5ce5e8 DH |
20 | #include <unistd.h> |
21 | ||
1f522a48 | 22 | #define MEMFD_STR "memfd:" |
3037aeb9 | 23 | #define MEMFD_HUGE_STR "memfd-hugetlb:" |
1f522a48 MK |
24 | #define SHARED_FT_STR "(shared file-table)" |
25 | ||
4f5ce5e8 | 26 | #define MFD_DEF_SIZE 8192 |
1f78dda2 | 27 | #define STACK_SIZE 65536 |
4f5ce5e8 | 28 | |
1f522a48 MK |
29 | /* |
30 | * Default is not to test hugetlbfs | |
31 | */ | |
32 | static int hugetlbfs_test; | |
33 | static size_t mfd_def_size = MFD_DEF_SIZE; | |
3037aeb9 | 34 | static const char *memfd_str = MEMFD_STR; |
1f522a48 MK |
35 | |
36 | /* | |
37 | * Copied from mlock2-tests.c | |
38 | */ | |
39 | static unsigned long default_huge_page_size(void) | |
40 | { | |
41 | unsigned long hps = 0; | |
42 | char *line = NULL; | |
43 | size_t linelen = 0; | |
44 | FILE *f = fopen("/proc/meminfo", "r"); | |
45 | ||
46 | if (!f) | |
47 | return 0; | |
48 | while (getline(&line, &linelen, f) > 0) { | |
49 | if (sscanf(line, "Hugepagesize: %lu kB", &hps) == 1) { | |
50 | hps <<= 10; | |
51 | break; | |
52 | } | |
53 | } | |
54 | ||
55 | free(line); | |
56 | fclose(f); | |
57 | return hps; | |
58 | } | |
59 | ||
4f5ce5e8 DH |
60 | static int sys_memfd_create(const char *name, |
61 | unsigned int flags) | |
62 | { | |
1f522a48 MK |
63 | if (hugetlbfs_test) |
64 | flags |= MFD_HUGETLB; | |
65 | ||
4f5ce5e8 DH |
66 | return syscall(__NR_memfd_create, name, flags); |
67 | } | |
68 | ||
69 | static int mfd_assert_new(const char *name, loff_t sz, unsigned int flags) | |
70 | { | |
71 | int r, fd; | |
72 | ||
73 | fd = sys_memfd_create(name, flags); | |
74 | if (fd < 0) { | |
75 | printf("memfd_create(\"%s\", %u) failed: %m\n", | |
76 | name, flags); | |
77 | abort(); | |
78 | } | |
79 | ||
80 | r = ftruncate(fd, sz); | |
81 | if (r < 0) { | |
82 | printf("ftruncate(%llu) failed: %m\n", (unsigned long long)sz); | |
83 | abort(); | |
84 | } | |
85 | ||
86 | return fd; | |
87 | } | |
88 | ||
89 | static void mfd_fail_new(const char *name, unsigned int flags) | |
90 | { | |
91 | int r; | |
92 | ||
93 | r = sys_memfd_create(name, flags); | |
94 | if (r >= 0) { | |
95 | printf("memfd_create(\"%s\", %u) succeeded, but failure expected\n", | |
96 | name, flags); | |
97 | close(r); | |
98 | abort(); | |
99 | } | |
100 | } | |
101 | ||
57e67900 | 102 | static unsigned int mfd_assert_get_seals(int fd) |
4f5ce5e8 | 103 | { |
57e67900 | 104 | int r; |
4f5ce5e8 DH |
105 | |
106 | r = fcntl(fd, F_GET_SEALS); | |
107 | if (r < 0) { | |
108 | printf("GET_SEALS(%d) failed: %m\n", fd); | |
109 | abort(); | |
110 | } | |
111 | ||
57e67900 | 112 | return (unsigned int)r; |
4f5ce5e8 DH |
113 | } |
114 | ||
57e67900 | 115 | static void mfd_assert_has_seals(int fd, unsigned int seals) |
4f5ce5e8 | 116 | { |
57e67900 | 117 | unsigned int s; |
4f5ce5e8 DH |
118 | |
119 | s = mfd_assert_get_seals(fd); | |
120 | if (s != seals) { | |
57e67900 | 121 | printf("%u != %u = GET_SEALS(%d)\n", seals, s, fd); |
4f5ce5e8 DH |
122 | abort(); |
123 | } | |
124 | } | |
125 | ||
57e67900 | 126 | static void mfd_assert_add_seals(int fd, unsigned int seals) |
4f5ce5e8 | 127 | { |
57e67900 PK |
128 | int r; |
129 | unsigned int s; | |
4f5ce5e8 DH |
130 | |
131 | s = mfd_assert_get_seals(fd); | |
132 | r = fcntl(fd, F_ADD_SEALS, seals); | |
133 | if (r < 0) { | |
57e67900 | 134 | printf("ADD_SEALS(%d, %u -> %u) failed: %m\n", fd, s, seals); |
4f5ce5e8 DH |
135 | abort(); |
136 | } | |
137 | } | |
138 | ||
57e67900 | 139 | static void mfd_fail_add_seals(int fd, unsigned int seals) |
4f5ce5e8 | 140 | { |
57e67900 PK |
141 | int r; |
142 | unsigned int s; | |
4f5ce5e8 DH |
143 | |
144 | r = fcntl(fd, F_GET_SEALS); | |
145 | if (r < 0) | |
146 | s = 0; | |
147 | else | |
57e67900 | 148 | s = (unsigned int)r; |
4f5ce5e8 DH |
149 | |
150 | r = fcntl(fd, F_ADD_SEALS, seals); | |
151 | if (r >= 0) { | |
57e67900 PK |
152 | printf("ADD_SEALS(%d, %u -> %u) didn't fail as expected\n", |
153 | fd, s, seals); | |
4f5ce5e8 DH |
154 | abort(); |
155 | } | |
156 | } | |
157 | ||
158 | static void mfd_assert_size(int fd, size_t size) | |
159 | { | |
160 | struct stat st; | |
161 | int r; | |
162 | ||
163 | r = fstat(fd, &st); | |
164 | if (r < 0) { | |
165 | printf("fstat(%d) failed: %m\n", fd); | |
166 | abort(); | |
167 | } else if (st.st_size != size) { | |
168 | printf("wrong file size %lld, but expected %lld\n", | |
169 | (long long)st.st_size, (long long)size); | |
170 | abort(); | |
171 | } | |
172 | } | |
173 | ||
174 | static int mfd_assert_dup(int fd) | |
175 | { | |
176 | int r; | |
177 | ||
178 | r = dup(fd); | |
179 | if (r < 0) { | |
180 | printf("dup(%d) failed: %m\n", fd); | |
181 | abort(); | |
182 | } | |
183 | ||
184 | return r; | |
185 | } | |
186 | ||
187 | static void *mfd_assert_mmap_shared(int fd) | |
188 | { | |
189 | void *p; | |
190 | ||
191 | p = mmap(NULL, | |
1f522a48 | 192 | mfd_def_size, |
4f5ce5e8 DH |
193 | PROT_READ | PROT_WRITE, |
194 | MAP_SHARED, | |
195 | fd, | |
196 | 0); | |
197 | if (p == MAP_FAILED) { | |
198 | printf("mmap() failed: %m\n"); | |
199 | abort(); | |
200 | } | |
201 | ||
202 | return p; | |
203 | } | |
204 | ||
205 | static void *mfd_assert_mmap_private(int fd) | |
206 | { | |
207 | void *p; | |
208 | ||
209 | p = mmap(NULL, | |
1f522a48 | 210 | mfd_def_size, |
4f5ce5e8 DH |
211 | PROT_READ, |
212 | MAP_PRIVATE, | |
213 | fd, | |
214 | 0); | |
215 | if (p == MAP_FAILED) { | |
216 | printf("mmap() failed: %m\n"); | |
217 | abort(); | |
218 | } | |
219 | ||
220 | return p; | |
221 | } | |
222 | ||
223 | static int mfd_assert_open(int fd, int flags, mode_t mode) | |
224 | { | |
225 | char buf[512]; | |
226 | int r; | |
227 | ||
228 | sprintf(buf, "/proc/self/fd/%d", fd); | |
229 | r = open(buf, flags, mode); | |
230 | if (r < 0) { | |
231 | printf("open(%s) failed: %m\n", buf); | |
232 | abort(); | |
233 | } | |
234 | ||
235 | return r; | |
236 | } | |
237 | ||
238 | static void mfd_fail_open(int fd, int flags, mode_t mode) | |
239 | { | |
240 | char buf[512]; | |
241 | int r; | |
242 | ||
243 | sprintf(buf, "/proc/self/fd/%d", fd); | |
244 | r = open(buf, flags, mode); | |
245 | if (r >= 0) { | |
2ed36928 | 246 | printf("open(%s) didn't fail as expected\n", buf); |
4f5ce5e8 DH |
247 | abort(); |
248 | } | |
249 | } | |
250 | ||
251 | static void mfd_assert_read(int fd) | |
252 | { | |
253 | char buf[16]; | |
254 | void *p; | |
255 | ssize_t l; | |
256 | ||
257 | l = read(fd, buf, sizeof(buf)); | |
258 | if (l != sizeof(buf)) { | |
259 | printf("read() failed: %m\n"); | |
260 | abort(); | |
261 | } | |
262 | ||
263 | /* verify PROT_READ *is* allowed */ | |
264 | p = mmap(NULL, | |
1f522a48 | 265 | mfd_def_size, |
4f5ce5e8 DH |
266 | PROT_READ, |
267 | MAP_PRIVATE, | |
268 | fd, | |
269 | 0); | |
270 | if (p == MAP_FAILED) { | |
271 | printf("mmap() failed: %m\n"); | |
272 | abort(); | |
273 | } | |
1f522a48 | 274 | munmap(p, mfd_def_size); |
4f5ce5e8 DH |
275 | |
276 | /* verify MAP_PRIVATE is *always* allowed (even writable) */ | |
277 | p = mmap(NULL, | |
1f522a48 | 278 | mfd_def_size, |
4f5ce5e8 DH |
279 | PROT_READ | PROT_WRITE, |
280 | MAP_PRIVATE, | |
281 | fd, | |
282 | 0); | |
283 | if (p == MAP_FAILED) { | |
284 | printf("mmap() failed: %m\n"); | |
285 | abort(); | |
286 | } | |
1f522a48 | 287 | munmap(p, mfd_def_size); |
4f5ce5e8 DH |
288 | } |
289 | ||
290 | static void mfd_assert_write(int fd) | |
291 | { | |
292 | ssize_t l; | |
293 | void *p; | |
294 | int r; | |
295 | ||
1f522a48 MK |
296 | /* |
297 | * huegtlbfs does not support write, but we want to | |
298 | * verify everything else here. | |
299 | */ | |
300 | if (!hugetlbfs_test) { | |
301 | /* verify write() succeeds */ | |
302 | l = write(fd, "\0\0\0\0", 4); | |
303 | if (l != 4) { | |
304 | printf("write() failed: %m\n"); | |
305 | abort(); | |
306 | } | |
4f5ce5e8 DH |
307 | } |
308 | ||
309 | /* verify PROT_READ | PROT_WRITE is allowed */ | |
310 | p = mmap(NULL, | |
1f522a48 | 311 | mfd_def_size, |
4f5ce5e8 DH |
312 | PROT_READ | PROT_WRITE, |
313 | MAP_SHARED, | |
314 | fd, | |
315 | 0); | |
316 | if (p == MAP_FAILED) { | |
317 | printf("mmap() failed: %m\n"); | |
318 | abort(); | |
319 | } | |
320 | *(char *)p = 0; | |
1f522a48 | 321 | munmap(p, mfd_def_size); |
4f5ce5e8 DH |
322 | |
323 | /* verify PROT_WRITE is allowed */ | |
324 | p = mmap(NULL, | |
1f522a48 | 325 | mfd_def_size, |
4f5ce5e8 DH |
326 | PROT_WRITE, |
327 | MAP_SHARED, | |
328 | fd, | |
329 | 0); | |
330 | if (p == MAP_FAILED) { | |
331 | printf("mmap() failed: %m\n"); | |
332 | abort(); | |
333 | } | |
334 | *(char *)p = 0; | |
1f522a48 | 335 | munmap(p, mfd_def_size); |
4f5ce5e8 DH |
336 | |
337 | /* verify PROT_READ with MAP_SHARED is allowed and a following | |
338 | * mprotect(PROT_WRITE) allows writing */ | |
339 | p = mmap(NULL, | |
1f522a48 | 340 | mfd_def_size, |
4f5ce5e8 DH |
341 | PROT_READ, |
342 | MAP_SHARED, | |
343 | fd, | |
344 | 0); | |
345 | if (p == MAP_FAILED) { | |
346 | printf("mmap() failed: %m\n"); | |
347 | abort(); | |
348 | } | |
349 | ||
1f522a48 | 350 | r = mprotect(p, mfd_def_size, PROT_READ | PROT_WRITE); |
4f5ce5e8 DH |
351 | if (r < 0) { |
352 | printf("mprotect() failed: %m\n"); | |
353 | abort(); | |
354 | } | |
355 | ||
356 | *(char *)p = 0; | |
1f522a48 | 357 | munmap(p, mfd_def_size); |
4f5ce5e8 DH |
358 | |
359 | /* verify PUNCH_HOLE works */ | |
360 | r = fallocate(fd, | |
361 | FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, | |
362 | 0, | |
1f522a48 | 363 | mfd_def_size); |
4f5ce5e8 DH |
364 | if (r < 0) { |
365 | printf("fallocate(PUNCH_HOLE) failed: %m\n"); | |
366 | abort(); | |
367 | } | |
368 | } | |
369 | ||
370 | static void mfd_fail_write(int fd) | |
371 | { | |
372 | ssize_t l; | |
373 | void *p; | |
374 | int r; | |
375 | ||
376 | /* verify write() fails */ | |
377 | l = write(fd, "data", 4); | |
378 | if (l != -EPERM) { | |
379 | printf("expected EPERM on write(), but got %d: %m\n", (int)l); | |
380 | abort(); | |
381 | } | |
382 | ||
383 | /* verify PROT_READ | PROT_WRITE is not allowed */ | |
384 | p = mmap(NULL, | |
1f522a48 | 385 | mfd_def_size, |
4f5ce5e8 DH |
386 | PROT_READ | PROT_WRITE, |
387 | MAP_SHARED, | |
388 | fd, | |
389 | 0); | |
390 | if (p != MAP_FAILED) { | |
391 | printf("mmap() didn't fail as expected\n"); | |
392 | abort(); | |
393 | } | |
394 | ||
395 | /* verify PROT_WRITE is not allowed */ | |
396 | p = mmap(NULL, | |
1f522a48 | 397 | mfd_def_size, |
4f5ce5e8 DH |
398 | PROT_WRITE, |
399 | MAP_SHARED, | |
400 | fd, | |
401 | 0); | |
402 | if (p != MAP_FAILED) { | |
403 | printf("mmap() didn't fail as expected\n"); | |
404 | abort(); | |
405 | } | |
406 | ||
407 | /* Verify PROT_READ with MAP_SHARED with a following mprotect is not | |
408 | * allowed. Note that for r/w the kernel already prevents the mmap. */ | |
409 | p = mmap(NULL, | |
1f522a48 | 410 | mfd_def_size, |
4f5ce5e8 DH |
411 | PROT_READ, |
412 | MAP_SHARED, | |
413 | fd, | |
414 | 0); | |
415 | if (p != MAP_FAILED) { | |
1f522a48 | 416 | r = mprotect(p, mfd_def_size, PROT_READ | PROT_WRITE); |
4f5ce5e8 DH |
417 | if (r >= 0) { |
418 | printf("mmap()+mprotect() didn't fail as expected\n"); | |
419 | abort(); | |
420 | } | |
421 | } | |
422 | ||
423 | /* verify PUNCH_HOLE fails */ | |
424 | r = fallocate(fd, | |
425 | FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, | |
426 | 0, | |
1f522a48 | 427 | mfd_def_size); |
4f5ce5e8 DH |
428 | if (r >= 0) { |
429 | printf("fallocate(PUNCH_HOLE) didn't fail as expected\n"); | |
430 | abort(); | |
431 | } | |
432 | } | |
433 | ||
434 | static void mfd_assert_shrink(int fd) | |
435 | { | |
436 | int r, fd2; | |
437 | ||
1f522a48 | 438 | r = ftruncate(fd, mfd_def_size / 2); |
4f5ce5e8 DH |
439 | if (r < 0) { |
440 | printf("ftruncate(SHRINK) failed: %m\n"); | |
441 | abort(); | |
442 | } | |
443 | ||
1f522a48 | 444 | mfd_assert_size(fd, mfd_def_size / 2); |
4f5ce5e8 DH |
445 | |
446 | fd2 = mfd_assert_open(fd, | |
447 | O_RDWR | O_CREAT | O_TRUNC, | |
448 | S_IRUSR | S_IWUSR); | |
449 | close(fd2); | |
450 | ||
451 | mfd_assert_size(fd, 0); | |
452 | } | |
453 | ||
454 | static void mfd_fail_shrink(int fd) | |
455 | { | |
456 | int r; | |
457 | ||
1f522a48 | 458 | r = ftruncate(fd, mfd_def_size / 2); |
4f5ce5e8 DH |
459 | if (r >= 0) { |
460 | printf("ftruncate(SHRINK) didn't fail as expected\n"); | |
461 | abort(); | |
462 | } | |
463 | ||
464 | mfd_fail_open(fd, | |
465 | O_RDWR | O_CREAT | O_TRUNC, | |
466 | S_IRUSR | S_IWUSR); | |
467 | } | |
468 | ||
469 | static void mfd_assert_grow(int fd) | |
470 | { | |
471 | int r; | |
472 | ||
1f522a48 | 473 | r = ftruncate(fd, mfd_def_size * 2); |
4f5ce5e8 DH |
474 | if (r < 0) { |
475 | printf("ftruncate(GROW) failed: %m\n"); | |
476 | abort(); | |
477 | } | |
478 | ||
1f522a48 | 479 | mfd_assert_size(fd, mfd_def_size * 2); |
4f5ce5e8 DH |
480 | |
481 | r = fallocate(fd, | |
482 | 0, | |
483 | 0, | |
1f522a48 | 484 | mfd_def_size * 4); |
4f5ce5e8 DH |
485 | if (r < 0) { |
486 | printf("fallocate(ALLOC) failed: %m\n"); | |
487 | abort(); | |
488 | } | |
489 | ||
1f522a48 | 490 | mfd_assert_size(fd, mfd_def_size * 4); |
4f5ce5e8 DH |
491 | } |
492 | ||
493 | static void mfd_fail_grow(int fd) | |
494 | { | |
495 | int r; | |
496 | ||
1f522a48 | 497 | r = ftruncate(fd, mfd_def_size * 2); |
4f5ce5e8 DH |
498 | if (r >= 0) { |
499 | printf("ftruncate(GROW) didn't fail as expected\n"); | |
500 | abort(); | |
501 | } | |
502 | ||
503 | r = fallocate(fd, | |
504 | 0, | |
505 | 0, | |
1f522a48 | 506 | mfd_def_size * 4); |
4f5ce5e8 DH |
507 | if (r >= 0) { |
508 | printf("fallocate(ALLOC) didn't fail as expected\n"); | |
509 | abort(); | |
510 | } | |
511 | } | |
512 | ||
513 | static void mfd_assert_grow_write(int fd) | |
514 | { | |
1f522a48 | 515 | static char *buf; |
4f5ce5e8 DH |
516 | ssize_t l; |
517 | ||
72497845 MAL |
518 | /* hugetlbfs does not support write */ |
519 | if (hugetlbfs_test) | |
520 | return; | |
521 | ||
1f522a48 MK |
522 | buf = malloc(mfd_def_size * 8); |
523 | if (!buf) { | |
7d33d2b5 | 524 | printf("malloc(%zu) failed: %m\n", mfd_def_size * 8); |
1f522a48 MK |
525 | abort(); |
526 | } | |
527 | ||
528 | l = pwrite(fd, buf, mfd_def_size * 8, 0); | |
529 | if (l != (mfd_def_size * 8)) { | |
4f5ce5e8 DH |
530 | printf("pwrite() failed: %m\n"); |
531 | abort(); | |
532 | } | |
533 | ||
1f522a48 | 534 | mfd_assert_size(fd, mfd_def_size * 8); |
4f5ce5e8 DH |
535 | } |
536 | ||
537 | static void mfd_fail_grow_write(int fd) | |
538 | { | |
1f522a48 | 539 | static char *buf; |
4f5ce5e8 DH |
540 | ssize_t l; |
541 | ||
72497845 MAL |
542 | /* hugetlbfs does not support write */ |
543 | if (hugetlbfs_test) | |
544 | return; | |
545 | ||
1f522a48 MK |
546 | buf = malloc(mfd_def_size * 8); |
547 | if (!buf) { | |
7d33d2b5 | 548 | printf("malloc(%zu) failed: %m\n", mfd_def_size * 8); |
1f522a48 MK |
549 | abort(); |
550 | } | |
551 | ||
552 | l = pwrite(fd, buf, mfd_def_size * 8, 0); | |
553 | if (l == (mfd_def_size * 8)) { | |
4f5ce5e8 DH |
554 | printf("pwrite() didn't fail as expected\n"); |
555 | abort(); | |
556 | } | |
557 | } | |
558 | ||
559 | static int idle_thread_fn(void *arg) | |
560 | { | |
561 | sigset_t set; | |
562 | int sig; | |
563 | ||
564 | /* dummy waiter; SIGTERM terminates us anyway */ | |
565 | sigemptyset(&set); | |
566 | sigaddset(&set, SIGTERM); | |
567 | sigwait(&set, &sig); | |
568 | ||
569 | return 0; | |
570 | } | |
571 | ||
572 | static pid_t spawn_idle_thread(unsigned int flags) | |
573 | { | |
574 | uint8_t *stack; | |
575 | pid_t pid; | |
576 | ||
577 | stack = malloc(STACK_SIZE); | |
578 | if (!stack) { | |
579 | printf("malloc(STACK_SIZE) failed: %m\n"); | |
580 | abort(); | |
581 | } | |
582 | ||
583 | pid = clone(idle_thread_fn, | |
584 | stack + STACK_SIZE, | |
585 | SIGCHLD | flags, | |
586 | NULL); | |
587 | if (pid < 0) { | |
588 | printf("clone() failed: %m\n"); | |
589 | abort(); | |
590 | } | |
591 | ||
592 | return pid; | |
593 | } | |
594 | ||
595 | static void join_idle_thread(pid_t pid) | |
596 | { | |
597 | kill(pid, SIGTERM); | |
598 | waitpid(pid, NULL, 0); | |
599 | } | |
600 | ||
601 | /* | |
602 | * Test memfd_create() syscall | |
603 | * Verify syscall-argument validation, including name checks, flag validation | |
604 | * and more. | |
605 | */ | |
606 | static void test_create(void) | |
607 | { | |
608 | char buf[2048]; | |
609 | int fd; | |
610 | ||
3037aeb9 | 611 | printf("%s CREATE\n", memfd_str); |
1f522a48 | 612 | |
4f5ce5e8 DH |
613 | /* test NULL name */ |
614 | mfd_fail_new(NULL, 0); | |
615 | ||
616 | /* test over-long name (not zero-terminated) */ | |
617 | memset(buf, 0xff, sizeof(buf)); | |
618 | mfd_fail_new(buf, 0); | |
619 | ||
620 | /* test over-long zero-terminated name */ | |
621 | memset(buf, 0xff, sizeof(buf)); | |
622 | buf[sizeof(buf) - 1] = 0; | |
623 | mfd_fail_new(buf, 0); | |
624 | ||
625 | /* verify "" is a valid name */ | |
626 | fd = mfd_assert_new("", 0, 0); | |
627 | close(fd); | |
628 | ||
629 | /* verify invalid O_* open flags */ | |
630 | mfd_fail_new("", 0x0100); | |
631 | mfd_fail_new("", ~MFD_CLOEXEC); | |
632 | mfd_fail_new("", ~MFD_ALLOW_SEALING); | |
633 | mfd_fail_new("", ~0); | |
634 | mfd_fail_new("", 0x80000000U); | |
635 | ||
636 | /* verify MFD_CLOEXEC is allowed */ | |
637 | fd = mfd_assert_new("", 0, MFD_CLOEXEC); | |
638 | close(fd); | |
639 | ||
72497845 MAL |
640 | /* verify MFD_ALLOW_SEALING is allowed */ |
641 | fd = mfd_assert_new("", 0, MFD_ALLOW_SEALING); | |
642 | close(fd); | |
643 | ||
644 | /* verify MFD_ALLOW_SEALING | MFD_CLOEXEC is allowed */ | |
645 | fd = mfd_assert_new("", 0, MFD_ALLOW_SEALING | MFD_CLOEXEC); | |
646 | close(fd); | |
4f5ce5e8 DH |
647 | } |
648 | ||
649 | /* | |
650 | * Test basic sealing | |
651 | * A very basic sealing test to see whether setting/retrieving seals works. | |
652 | */ | |
653 | static void test_basic(void) | |
654 | { | |
655 | int fd; | |
656 | ||
3037aeb9 | 657 | printf("%s BASIC\n", memfd_str); |
1f522a48 | 658 | |
4f5ce5e8 | 659 | fd = mfd_assert_new("kern_memfd_basic", |
1f522a48 | 660 | mfd_def_size, |
4f5ce5e8 DH |
661 | MFD_CLOEXEC | MFD_ALLOW_SEALING); |
662 | ||
663 | /* add basic seals */ | |
664 | mfd_assert_has_seals(fd, 0); | |
665 | mfd_assert_add_seals(fd, F_SEAL_SHRINK | | |
666 | F_SEAL_WRITE); | |
667 | mfd_assert_has_seals(fd, F_SEAL_SHRINK | | |
668 | F_SEAL_WRITE); | |
669 | ||
670 | /* add them again */ | |
671 | mfd_assert_add_seals(fd, F_SEAL_SHRINK | | |
672 | F_SEAL_WRITE); | |
673 | mfd_assert_has_seals(fd, F_SEAL_SHRINK | | |
674 | F_SEAL_WRITE); | |
675 | ||
676 | /* add more seals and seal against sealing */ | |
677 | mfd_assert_add_seals(fd, F_SEAL_GROW | F_SEAL_SEAL); | |
678 | mfd_assert_has_seals(fd, F_SEAL_SHRINK | | |
679 | F_SEAL_GROW | | |
680 | F_SEAL_WRITE | | |
681 | F_SEAL_SEAL); | |
682 | ||
683 | /* verify that sealing no longer works */ | |
684 | mfd_fail_add_seals(fd, F_SEAL_GROW); | |
685 | mfd_fail_add_seals(fd, 0); | |
686 | ||
687 | close(fd); | |
688 | ||
689 | /* verify sealing does not work without MFD_ALLOW_SEALING */ | |
690 | fd = mfd_assert_new("kern_memfd_basic", | |
1f522a48 | 691 | mfd_def_size, |
4f5ce5e8 DH |
692 | MFD_CLOEXEC); |
693 | mfd_assert_has_seals(fd, F_SEAL_SEAL); | |
694 | mfd_fail_add_seals(fd, F_SEAL_SHRINK | | |
695 | F_SEAL_GROW | | |
696 | F_SEAL_WRITE); | |
697 | mfd_assert_has_seals(fd, F_SEAL_SEAL); | |
698 | close(fd); | |
699 | } | |
700 | ||
701 | /* | |
702 | * Test SEAL_WRITE | |
703 | * Test whether SEAL_WRITE actually prevents modifications. | |
704 | */ | |
705 | static void test_seal_write(void) | |
706 | { | |
707 | int fd; | |
708 | ||
3037aeb9 | 709 | printf("%s SEAL-WRITE\n", memfd_str); |
1f522a48 | 710 | |
4f5ce5e8 | 711 | fd = mfd_assert_new("kern_memfd_seal_write", |
1f522a48 | 712 | mfd_def_size, |
4f5ce5e8 DH |
713 | MFD_CLOEXEC | MFD_ALLOW_SEALING); |
714 | mfd_assert_has_seals(fd, 0); | |
715 | mfd_assert_add_seals(fd, F_SEAL_WRITE); | |
716 | mfd_assert_has_seals(fd, F_SEAL_WRITE); | |
717 | ||
718 | mfd_assert_read(fd); | |
719 | mfd_fail_write(fd); | |
720 | mfd_assert_shrink(fd); | |
721 | mfd_assert_grow(fd); | |
722 | mfd_fail_grow_write(fd); | |
723 | ||
724 | close(fd); | |
725 | } | |
726 | ||
727 | /* | |
728 | * Test SEAL_SHRINK | |
729 | * Test whether SEAL_SHRINK actually prevents shrinking | |
730 | */ | |
731 | static void test_seal_shrink(void) | |
732 | { | |
733 | int fd; | |
734 | ||
3037aeb9 | 735 | printf("%s SEAL-SHRINK\n", memfd_str); |
1f522a48 | 736 | |
4f5ce5e8 | 737 | fd = mfd_assert_new("kern_memfd_seal_shrink", |
1f522a48 | 738 | mfd_def_size, |
4f5ce5e8 DH |
739 | MFD_CLOEXEC | MFD_ALLOW_SEALING); |
740 | mfd_assert_has_seals(fd, 0); | |
741 | mfd_assert_add_seals(fd, F_SEAL_SHRINK); | |
742 | mfd_assert_has_seals(fd, F_SEAL_SHRINK); | |
743 | ||
744 | mfd_assert_read(fd); | |
745 | mfd_assert_write(fd); | |
746 | mfd_fail_shrink(fd); | |
747 | mfd_assert_grow(fd); | |
748 | mfd_assert_grow_write(fd); | |
749 | ||
750 | close(fd); | |
751 | } | |
752 | ||
753 | /* | |
754 | * Test SEAL_GROW | |
755 | * Test whether SEAL_GROW actually prevents growing | |
756 | */ | |
757 | static void test_seal_grow(void) | |
758 | { | |
759 | int fd; | |
760 | ||
3037aeb9 | 761 | printf("%s SEAL-GROW\n", memfd_str); |
1f522a48 | 762 | |
4f5ce5e8 | 763 | fd = mfd_assert_new("kern_memfd_seal_grow", |
1f522a48 | 764 | mfd_def_size, |
4f5ce5e8 DH |
765 | MFD_CLOEXEC | MFD_ALLOW_SEALING); |
766 | mfd_assert_has_seals(fd, 0); | |
767 | mfd_assert_add_seals(fd, F_SEAL_GROW); | |
768 | mfd_assert_has_seals(fd, F_SEAL_GROW); | |
769 | ||
770 | mfd_assert_read(fd); | |
771 | mfd_assert_write(fd); | |
772 | mfd_assert_shrink(fd); | |
773 | mfd_fail_grow(fd); | |
774 | mfd_fail_grow_write(fd); | |
775 | ||
776 | close(fd); | |
777 | } | |
778 | ||
779 | /* | |
780 | * Test SEAL_SHRINK | SEAL_GROW | |
781 | * Test whether SEAL_SHRINK | SEAL_GROW actually prevents resizing | |
782 | */ | |
783 | static void test_seal_resize(void) | |
784 | { | |
785 | int fd; | |
786 | ||
3037aeb9 | 787 | printf("%s SEAL-RESIZE\n", memfd_str); |
1f522a48 | 788 | |
4f5ce5e8 | 789 | fd = mfd_assert_new("kern_memfd_seal_resize", |
1f522a48 | 790 | mfd_def_size, |
4f5ce5e8 DH |
791 | MFD_CLOEXEC | MFD_ALLOW_SEALING); |
792 | mfd_assert_has_seals(fd, 0); | |
793 | mfd_assert_add_seals(fd, F_SEAL_SHRINK | F_SEAL_GROW); | |
794 | mfd_assert_has_seals(fd, F_SEAL_SHRINK | F_SEAL_GROW); | |
795 | ||
796 | mfd_assert_read(fd); | |
797 | mfd_assert_write(fd); | |
798 | mfd_fail_shrink(fd); | |
799 | mfd_fail_grow(fd); | |
800 | mfd_fail_grow_write(fd); | |
801 | ||
802 | close(fd); | |
803 | } | |
804 | ||
805 | /* | |
806 | * Test sharing via dup() | |
807 | * Test that seals are shared between dupped FDs and they're all equal. | |
808 | */ | |
1f522a48 | 809 | static void test_share_dup(char *banner, char *b_suffix) |
4f5ce5e8 DH |
810 | { |
811 | int fd, fd2; | |
812 | ||
3037aeb9 | 813 | printf("%s %s %s\n", memfd_str, banner, b_suffix); |
1f522a48 | 814 | |
4f5ce5e8 | 815 | fd = mfd_assert_new("kern_memfd_share_dup", |
1f522a48 | 816 | mfd_def_size, |
4f5ce5e8 DH |
817 | MFD_CLOEXEC | MFD_ALLOW_SEALING); |
818 | mfd_assert_has_seals(fd, 0); | |
819 | ||
820 | fd2 = mfd_assert_dup(fd); | |
821 | mfd_assert_has_seals(fd2, 0); | |
822 | ||
823 | mfd_assert_add_seals(fd, F_SEAL_WRITE); | |
824 | mfd_assert_has_seals(fd, F_SEAL_WRITE); | |
825 | mfd_assert_has_seals(fd2, F_SEAL_WRITE); | |
826 | ||
827 | mfd_assert_add_seals(fd2, F_SEAL_SHRINK); | |
828 | mfd_assert_has_seals(fd, F_SEAL_WRITE | F_SEAL_SHRINK); | |
829 | mfd_assert_has_seals(fd2, F_SEAL_WRITE | F_SEAL_SHRINK); | |
830 | ||
831 | mfd_assert_add_seals(fd, F_SEAL_SEAL); | |
832 | mfd_assert_has_seals(fd, F_SEAL_WRITE | F_SEAL_SHRINK | F_SEAL_SEAL); | |
833 | mfd_assert_has_seals(fd2, F_SEAL_WRITE | F_SEAL_SHRINK | F_SEAL_SEAL); | |
834 | ||
835 | mfd_fail_add_seals(fd, F_SEAL_GROW); | |
836 | mfd_fail_add_seals(fd2, F_SEAL_GROW); | |
837 | mfd_fail_add_seals(fd, F_SEAL_SEAL); | |
838 | mfd_fail_add_seals(fd2, F_SEAL_SEAL); | |
839 | ||
840 | close(fd2); | |
841 | ||
842 | mfd_fail_add_seals(fd, F_SEAL_GROW); | |
843 | close(fd); | |
844 | } | |
845 | ||
846 | /* | |
847 | * Test sealing with active mmap()s | |
848 | * Modifying seals is only allowed if no other mmap() refs exist. | |
849 | */ | |
1f522a48 | 850 | static void test_share_mmap(char *banner, char *b_suffix) |
4f5ce5e8 DH |
851 | { |
852 | int fd; | |
853 | void *p; | |
854 | ||
3037aeb9 | 855 | printf("%s %s %s\n", memfd_str, banner, b_suffix); |
1f522a48 | 856 | |
4f5ce5e8 | 857 | fd = mfd_assert_new("kern_memfd_share_mmap", |
1f522a48 | 858 | mfd_def_size, |
4f5ce5e8 DH |
859 | MFD_CLOEXEC | MFD_ALLOW_SEALING); |
860 | mfd_assert_has_seals(fd, 0); | |
861 | ||
862 | /* shared/writable ref prevents sealing WRITE, but allows others */ | |
863 | p = mfd_assert_mmap_shared(fd); | |
864 | mfd_fail_add_seals(fd, F_SEAL_WRITE); | |
865 | mfd_assert_has_seals(fd, 0); | |
866 | mfd_assert_add_seals(fd, F_SEAL_SHRINK); | |
867 | mfd_assert_has_seals(fd, F_SEAL_SHRINK); | |
1f522a48 | 868 | munmap(p, mfd_def_size); |
4f5ce5e8 DH |
869 | |
870 | /* readable ref allows sealing */ | |
871 | p = mfd_assert_mmap_private(fd); | |
872 | mfd_assert_add_seals(fd, F_SEAL_WRITE); | |
873 | mfd_assert_has_seals(fd, F_SEAL_WRITE | F_SEAL_SHRINK); | |
1f522a48 MK |
874 | munmap(p, mfd_def_size); |
875 | ||
876 | close(fd); | |
877 | } | |
878 | ||
4f5ce5e8 DH |
879 | /* |
880 | * Test sealing with open(/proc/self/fd/%d) | |
881 | * Via /proc we can get access to a separate file-context for the same memfd. | |
882 | * This is *not* like dup(), but like a real separate open(). Make sure the | |
883 | * semantics are as expected and we correctly check for RDONLY / WRONLY / RDWR. | |
884 | */ | |
1f522a48 | 885 | static void test_share_open(char *banner, char *b_suffix) |
4f5ce5e8 DH |
886 | { |
887 | int fd, fd2; | |
888 | ||
3037aeb9 | 889 | printf("%s %s %s\n", memfd_str, banner, b_suffix); |
1f522a48 | 890 | |
4f5ce5e8 | 891 | fd = mfd_assert_new("kern_memfd_share_open", |
1f522a48 | 892 | mfd_def_size, |
4f5ce5e8 DH |
893 | MFD_CLOEXEC | MFD_ALLOW_SEALING); |
894 | mfd_assert_has_seals(fd, 0); | |
895 | ||
896 | fd2 = mfd_assert_open(fd, O_RDWR, 0); | |
897 | mfd_assert_add_seals(fd, F_SEAL_WRITE); | |
898 | mfd_assert_has_seals(fd, F_SEAL_WRITE); | |
899 | mfd_assert_has_seals(fd2, F_SEAL_WRITE); | |
900 | ||
901 | mfd_assert_add_seals(fd2, F_SEAL_SHRINK); | |
902 | mfd_assert_has_seals(fd, F_SEAL_WRITE | F_SEAL_SHRINK); | |
903 | mfd_assert_has_seals(fd2, F_SEAL_WRITE | F_SEAL_SHRINK); | |
904 | ||
905 | close(fd); | |
906 | fd = mfd_assert_open(fd2, O_RDONLY, 0); | |
907 | ||
908 | mfd_fail_add_seals(fd, F_SEAL_SEAL); | |
909 | mfd_assert_has_seals(fd, F_SEAL_WRITE | F_SEAL_SHRINK); | |
910 | mfd_assert_has_seals(fd2, F_SEAL_WRITE | F_SEAL_SHRINK); | |
911 | ||
912 | close(fd2); | |
913 | fd2 = mfd_assert_open(fd, O_RDWR, 0); | |
914 | ||
915 | mfd_assert_add_seals(fd2, F_SEAL_SEAL); | |
916 | mfd_assert_has_seals(fd, F_SEAL_WRITE | F_SEAL_SHRINK | F_SEAL_SEAL); | |
917 | mfd_assert_has_seals(fd2, F_SEAL_WRITE | F_SEAL_SHRINK | F_SEAL_SEAL); | |
918 | ||
919 | close(fd2); | |
920 | close(fd); | |
921 | } | |
922 | ||
923 | /* | |
924 | * Test sharing via fork() | |
925 | * Test whether seal-modifications work as expected with forked childs. | |
926 | */ | |
1f522a48 | 927 | static void test_share_fork(char *banner, char *b_suffix) |
4f5ce5e8 DH |
928 | { |
929 | int fd; | |
930 | pid_t pid; | |
931 | ||
3037aeb9 | 932 | printf("%s %s %s\n", memfd_str, banner, b_suffix); |
1f522a48 | 933 | |
4f5ce5e8 | 934 | fd = mfd_assert_new("kern_memfd_share_fork", |
1f522a48 | 935 | mfd_def_size, |
4f5ce5e8 DH |
936 | MFD_CLOEXEC | MFD_ALLOW_SEALING); |
937 | mfd_assert_has_seals(fd, 0); | |
938 | ||
939 | pid = spawn_idle_thread(0); | |
940 | mfd_assert_add_seals(fd, F_SEAL_SEAL); | |
941 | mfd_assert_has_seals(fd, F_SEAL_SEAL); | |
942 | ||
943 | mfd_fail_add_seals(fd, F_SEAL_WRITE); | |
944 | mfd_assert_has_seals(fd, F_SEAL_SEAL); | |
945 | ||
946 | join_idle_thread(pid); | |
947 | ||
948 | mfd_fail_add_seals(fd, F_SEAL_WRITE); | |
949 | mfd_assert_has_seals(fd, F_SEAL_SEAL); | |
950 | ||
951 | close(fd); | |
952 | } | |
953 | ||
954 | int main(int argc, char **argv) | |
955 | { | |
956 | pid_t pid; | |
957 | ||
1f522a48 MK |
958 | if (argc == 2) { |
959 | if (!strcmp(argv[1], "hugetlbfs")) { | |
960 | unsigned long hpage_size = default_huge_page_size(); | |
961 | ||
962 | if (!hpage_size) { | |
963 | printf("Unable to determine huge page size\n"); | |
964 | abort(); | |
965 | } | |
966 | ||
967 | hugetlbfs_test = 1; | |
3037aeb9 | 968 | memfd_str = MEMFD_HUGE_STR; |
1f522a48 | 969 | mfd_def_size = hpage_size * 2; |
3037aeb9 MAL |
970 | } else { |
971 | printf("Unknown option: %s\n", argv[1]); | |
972 | abort(); | |
1f522a48 MK |
973 | } |
974 | } | |
975 | ||
4f5ce5e8 | 976 | test_create(); |
4f5ce5e8 DH |
977 | test_basic(); |
978 | ||
4f5ce5e8 | 979 | test_seal_write(); |
4f5ce5e8 | 980 | test_seal_shrink(); |
4f5ce5e8 | 981 | test_seal_grow(); |
4f5ce5e8 DH |
982 | test_seal_resize(); |
983 | ||
1f522a48 MK |
984 | test_share_dup("SHARE-DUP", ""); |
985 | test_share_mmap("SHARE-MMAP", ""); | |
986 | test_share_open("SHARE-OPEN", ""); | |
987 | test_share_fork("SHARE-FORK", ""); | |
4f5ce5e8 DH |
988 | |
989 | /* Run test-suite in a multi-threaded environment with a shared | |
990 | * file-table. */ | |
991 | pid = spawn_idle_thread(CLONE_FILES | CLONE_FS | CLONE_VM); | |
1f522a48 MK |
992 | test_share_dup("SHARE-DUP", SHARED_FT_STR); |
993 | test_share_mmap("SHARE-MMAP", SHARED_FT_STR); | |
994 | test_share_open("SHARE-OPEN", SHARED_FT_STR); | |
995 | test_share_fork("SHARE-FORK", SHARED_FT_STR); | |
4f5ce5e8 DH |
996 | join_idle_thread(pid); |
997 | ||
998 | printf("memfd: DONE\n"); | |
999 | ||
1000 | return 0; | |
1001 | } |