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