1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (C) 2017 Red Hat, Inc.
4 */
5
6 #define _GNU_SOURCE
7
8 #include <sys/types.h>
9 #include <sys/syscall.h>
10 #include <sys/uio.h>
11 #include <errno.h>
12 #include <string.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <unistd.h>
16
17 #define TST_NO_DEFAULT_MAIN
18 #include "tst_test.h"
19 #include "lapi/fallocate.h"
20 #include "lapi/fcntl.h"
21 #include "lapi/memfd.h"
22
23 #include "lapi/syscalls.h"
24
25 #include "memfd_create_common.h"
26
sys_memfd_create(const char * name,unsigned int flags)27 int sys_memfd_create(const char *name, unsigned int flags)
28 {
29 return tst_syscall(__NR_memfd_create, name, flags);
30 }
31
check_fallocate(const char * filename,const int lineno,int fd,int mode,off_t offset,off_t len)32 int check_fallocate(const char *filename, const int lineno, int fd,
33 int mode, off_t offset, off_t len)
34 {
35 int r;
36
37 r = fallocate(fd, mode, offset, len);
38 if (r < 0) {
39 tst_brk_(filename, lineno, TFAIL | TERRNO,
40 "fallocate(%d, %d, %ld, %ld) failed", fd, mode,
41 offset, len);
42 }
43
44 tst_res_(filename, lineno, TPASS,
45 "fallocate(%d, %d, %ld, %ld) succeeded", fd, mode,
46 offset, len);
47
48 return r;
49 }
50
check_fallocate_fail(const char * filename,const int lineno,int fd,int mode,off_t offset,off_t len)51 int check_fallocate_fail(const char *filename, const int lineno, int fd,
52 int mode, off_t offset, off_t len)
53 {
54 int r;
55
56 r = fallocate(fd, mode, offset, len);
57 if (r >= 0) {
58 tst_res_(filename, lineno, TFAIL,
59 "fallocate(%d, %d, %ld, %ld) succeeded unexpectedly",
60 fd, mode, offset, len);
61
62 return r;
63 }
64
65 tst_res_(filename, lineno, TPASS | TERRNO,
66 "fallocate(%d, %d, %ld, %ld) failed as expected", fd,
67 mode, offset, len);
68
69 return r;
70 }
71
check_ftruncate(const char * filename,const int lineno,int fd,off_t length)72 void check_ftruncate(const char *filename, const int lineno, int fd,
73 off_t length)
74 {
75 safe_ftruncate(filename, lineno, fd, length);
76
77 tst_res_(filename, lineno, TPASS, "ftruncate(%d, %ld) succeeded", fd,
78 length);
79 }
80
check_ftruncate_fail(const char * filename,const int lineno,int fd,off_t length)81 void check_ftruncate_fail(const char *filename, const int lineno,
82 int fd, off_t length)
83 {
84 if (ftruncate(fd, length) >= 0) {
85 tst_res_(filename, lineno, TFAIL,
86 "ftruncate(%d, %ld) succeeded unexpectedly",
87 fd, length);
88
89 return;
90 }
91
92 tst_res_(filename, lineno, TPASS | TERRNO,
93 "ftruncate(%d, %ld) failed as expected", fd, length);
94 }
95
get_mfd_all_available_flags(const char * filename,const int lineno)96 int get_mfd_all_available_flags(const char *filename, const int lineno)
97 {
98 unsigned int i;
99 int flag;
100 int flags2test[] = FLAGS_ALL_ARRAY_INITIALIZER;
101 int flags_available = 0;
102
103 if (!MFD_FLAGS_AVAILABLE(0)) {
104 tst_brk_(filename, lineno, TCONF,
105 "memfd_create(0) not implemented");
106 }
107
108 for (i = 0; i < ARRAY_SIZE(flags2test); i++) {
109 flag = flags2test[i];
110
111 if (MFD_FLAGS_AVAILABLE(flag))
112 flags_available |= flag;
113 }
114
115 return flags_available;
116 }
117
mfd_flags_available(const char * filename,const int lineno,unsigned int flags)118 int mfd_flags_available(const char *filename, const int lineno,
119 unsigned int flags)
120 {
121 TEST(sys_memfd_create("dummy_call", flags));
122 if (TST_RET < 0) {
123 if (TST_ERR != EINVAL) {
124 tst_brk_(filename, lineno, TBROK | TTERRNO,
125 "memfd_create() failed");
126 }
127
128 return 0;
129 }
130
131 SAFE_CLOSE(TST_RET);
132
133 return 1;
134 }
135
check_mfd_new(const char * filename,const int lineno,const char * name,loff_t sz,int flags)136 int check_mfd_new(const char *filename, const int lineno,
137 const char *name, loff_t sz, int flags)
138 {
139 int fd;
140
141 fd = sys_memfd_create(name, flags);
142 if (fd < 0) {
143 tst_brk_(filename, lineno, TBROK | TERRNO,
144 "memfd_create(%s, %d) failed", name, flags);
145 }
146
147 tst_res_(filename, lineno, TPASS, "memfd_create(%s, %d) succeeded",
148 name, flags);
149
150 check_ftruncate(filename, lineno, fd, sz);
151
152 return fd;
153 }
154
check_mfd_fail_new(const char * filename,const int lineno,const char * name,int flags)155 void check_mfd_fail_new(const char *filename, const int lineno,
156 const char *name, int flags)
157 {
158 int fd;
159
160 fd = sys_memfd_create(name, flags);
161 if (fd >= 0) {
162 safe_close(filename, lineno, NULL, fd);
163 tst_brk_(filename, lineno, TFAIL,
164 "memfd_create(%s, %d) succeeded unexpectedly",
165 name, flags);
166 }
167
168 tst_res_(filename, lineno, TPASS | TERRNO,
169 "memfd_create(%s, %d) failed as expected", name, flags);
170 }
171
check_mmap(const char * file,const int lineno,void * addr,size_t length,int prot,int flags,int fd,off_t offset)172 void *check_mmap(const char *file, const int lineno, void *addr, size_t length,
173 int prot, int flags, int fd, off_t offset)
174 {
175 void *p;
176
177 p = safe_mmap(file, lineno, addr, length, prot, flags, fd, offset);
178
179 tst_res_(file, lineno, TPASS,
180 "mmap(%p, %zu, %i, %i, %i, %li) succeeded", addr,
181 length, prot, flags, fd, (long)offset);
182
183 return p;
184 }
185
check_mmap_fail(const char * file,const int lineno,void * addr,size_t length,int prot,int flags,int fd,off_t offset)186 void check_mmap_fail(const char *file, const int lineno, void *addr,
187 size_t length, int prot, int flags, int fd, off_t offset)
188 {
189 if (mmap(addr, length, prot, flags, fd, offset) != MAP_FAILED) {
190 safe_munmap(file, lineno, NULL, addr, length);
191 tst_res_(file, lineno, TFAIL,
192 "mmap(%p, %zu, %i, %i, %i, %li) succeeded unexpectedly",
193 addr, length, prot, flags, fd, (long)offset);
194
195 return;
196 }
197
198 tst_res_(file, lineno, TPASS | TERRNO,
199 "mmap(%p, %zu, %i, %i, %i, %li) failed as expected",
200 addr, length, prot, flags, fd, (long)offset);
201 }
202
check_munmap(const char * file,const int lineno,void * p,size_t length)203 void check_munmap(const char *file, const int lineno, void *p, size_t length)
204 {
205 safe_munmap(file, lineno, NULL, p, length);
206
207 tst_res_(file, lineno, TPASS, "munmap(%p, %zu) succeeded", p, length);
208 }
209
check_mfd_has_seals(const char * file,const int lineno,int fd,int seals)210 void check_mfd_has_seals(const char *file, const int lineno, int fd, int seals)
211 {
212 int ret = SAFE_FCNTL((fd), F_GET_SEALS);
213 if (ret != seals) {
214 tst_brk_(file, lineno, TFAIL,
215 "fd %d doesn't have expected seals (%d expected %d)",
216 fd, ret, seals);
217 }
218
219 tst_res_(file, lineno, TPASS,
220 "fd %d has expected seals (%d)", fd, seals);
221 }
222
check_mprotect(const char * file,const int lineno,void * addr,size_t length,int prot)223 void check_mprotect(const char *file, const int lineno, void *addr,
224 size_t length, int prot)
225 {
226 if (mprotect(addr, length, prot) < 0) {
227 tst_brk_(file, lineno, TFAIL | TERRNO,
228 "mprotect(%p, %zu, %d) failed", addr, length, prot);
229 }
230
231 tst_res_(file, lineno, TPASS, "mprotect(%p, %zu, %d) succeeded", addr,
232 length, prot);
233 }
234
check_mfd_fail_add_seals(const char * filename,const int lineno,int fd,int seals)235 void check_mfd_fail_add_seals(const char *filename, const int lineno,
236 int fd, int seals)
237 {
238 if (fcntl(fd, F_ADD_SEALS, seals) >= 0) {
239 tst_brk_(filename, lineno, TFAIL,
240 "fcntl(%d, F_ADD_SEALS) succeeded unexpectedly", fd);
241 }
242
243 tst_res_(filename, lineno, TPASS | TERRNO,
244 "fcntl(%d, F_ADD_SEALS, %d) failed as expected", (fd),
245 (seals));
246 }
247
check_mfd_size(const char * filename,const int lineno,int fd,size_t size)248 void check_mfd_size(const char *filename, const int lineno, int fd,
249 size_t size)
250 {
251 struct stat st;
252
253 safe_fstat(filename, lineno, fd, &st);
254
255 if (st.st_size != (long)size) {
256 tst_brk_(filename, lineno, TFAIL,
257 "fstat(%d, &st): unexpected file size", fd);
258 }
259
260 tst_res_(filename, lineno, TPASS,
261 "fstat(%d, &st): file size is correct", fd);
262 }
263
check_mfd_open(const char * filename,const int lineno,int fd,int flags,mode_t mode)264 int check_mfd_open(const char *filename, const int lineno, int fd,
265 int flags, mode_t mode)
266 {
267 int r;
268 char buf[512];
269
270 sprintf(buf, "/proc/self/fd/%d", fd);
271
272 r = safe_open(filename, lineno, NULL, buf, flags, mode);
273
274 tst_res_(filename, lineno, TPASS, "open(%s, %d, %d) succeeded", buf,
275 flags, mode);
276
277 return r;
278 }
279
check_mfd_fail_open(const char * filename,const int lineno,int fd,int flags,mode_t mode)280 void check_mfd_fail_open(const char *filename, const int lineno, int fd,
281 int flags, mode_t mode)
282 {
283 char buf[512];
284
285 sprintf(buf, "/proc/self/fd/%d", fd);
286
287 fd = open(buf, flags, mode);
288 if (fd > 0) {
289 safe_close(filename, lineno, NULL, fd);
290 tst_res_(filename, lineno, TFAIL,
291 "open(%s, %d, %d) succeeded unexpectedly", buf,
292 flags, mode);
293 } else {
294 tst_res_(filename, lineno, TPASS | TERRNO,
295 "open(%s, %d, %d) failed as expected", buf,
296 flags, mode);
297 }
298 }
299
check_mfd_readable(const char * filename,const int lineno,int fd)300 void check_mfd_readable(const char *filename, const int lineno, int fd)
301 {
302 char buf[16];
303 void *p;
304
305 safe_read(filename, lineno, NULL, 1, fd, buf, sizeof(buf));
306 tst_res_(filename, lineno, TPASS, "read(%d, %s, %zu) succeeded", fd,
307 buf, sizeof(buf));
308
309 /* verify PROT_READ *is* allowed */
310 p = check_mmap(filename, lineno, NULL, MFD_DEF_SIZE, PROT_READ,
311 MAP_PRIVATE, fd, 0);
312
313 check_munmap(filename, lineno, p, MFD_DEF_SIZE);
314
315 /* verify MAP_PRIVATE is *always* allowed (even writable) */
316 p = check_mmap(filename, lineno, NULL, MFD_DEF_SIZE,
317 PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
318
319 check_munmap(filename, lineno, p, MFD_DEF_SIZE);
320 }
321
check_mfd_writeable(const char * filename,const int lineno,int fd)322 void check_mfd_writeable(const char *filename, const int lineno, int fd)
323 {
324 void *p;
325
326 /* verify write() succeeds */
327 safe_write(filename, lineno, NULL, 1, fd, "\0\0\0\0", 4);
328 tst_res_(filename, lineno, TPASS, "write(%d, %s, %d) succeeded", fd,
329 "\\0\\0\\0\\0", 4);
330
331 /* verify PROT_READ | PROT_WRITE is allowed */
332 p = check_mmap(filename, lineno, NULL, MFD_DEF_SIZE,
333 PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
334
335 *(char *)p = 0;
336 check_munmap(filename, lineno, p, MFD_DEF_SIZE);
337
338 /* verify PROT_WRITE is allowed */
339 p = check_mmap(filename, lineno, NULL, MFD_DEF_SIZE,
340 PROT_WRITE, MAP_SHARED, fd, 0);
341
342 *(char *)p = 0;
343 check_munmap(filename, lineno, p, MFD_DEF_SIZE);
344
345 /* verify PROT_READ with MAP_SHARED is allowed and a following
346 * mprotect(PROT_WRITE) allows writing
347 */
348 p = check_mmap(filename, lineno, NULL, MFD_DEF_SIZE,
349 PROT_READ, MAP_SHARED, fd, 0);
350
351 check_mprotect(filename, lineno, p, MFD_DEF_SIZE,
352 PROT_READ | PROT_WRITE);
353
354 *(char *)p = 0;
355 check_munmap(filename, lineno, p, MFD_DEF_SIZE);
356
357 /* verify PUNCH_HOLE works */
358 check_fallocate(filename, lineno, fd,
359 FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, 0,
360 MFD_DEF_SIZE);
361 }
362
check_mfd_non_writeable(const char * filename,const int lineno,int fd)363 void check_mfd_non_writeable(const char *filename, const int lineno,
364 int fd)
365 {
366 void *p;
367
368 /* verify write() fails */
369 TEST(write(fd, "data", 4));
370 if (TST_RET < 0) {
371 if (TST_ERR != EPERM) {
372 tst_brk_(filename, lineno, TFAIL | TTERRNO,
373 "write() didn't fail as expected");
374 }
375 } else {
376 tst_brk_(filename, lineno, TFAIL,
377 "write() succeeded unexpectedly");
378 }
379 tst_res_(filename, lineno, TPASS | TTERRNO, "write failed as expected");
380
381 /* verify PROT_READ | PROT_WRITE is not allowed */
382 check_mmap_fail(filename, lineno, NULL, MFD_DEF_SIZE,
383 PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
384
385 /* verify PROT_WRITE is not allowed */
386 check_mmap_fail(filename, lineno, NULL, MFD_DEF_SIZE,
387 PROT_WRITE, MAP_SHARED, fd, 0);
388
389 /* Verify PROT_READ with MAP_SHARED with a following mprotect is not
390 * allowed. Note that for r/w the kernel already prevents the mmap.
391 */
392 p = mmap(NULL, MFD_DEF_SIZE, PROT_READ, MAP_SHARED, fd, 0);
393 if (p != MAP_FAILED) {
394 if (mprotect(p, MFD_DEF_SIZE, PROT_READ | PROT_WRITE) >= 0) {
395 tst_brk_(filename, lineno, TFAIL | TERRNO,
396 "mmap()+mprotect() succeeded unexpectedly");
397 }
398 }
399
400 /* verify PUNCH_HOLE fails */
401 check_fallocate_fail(filename, lineno, fd,
402 FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, 0,
403 MFD_DEF_SIZE);
404 }
405
check_mfd_shrinkable(const char * filename,const int lineno,int fd)406 void check_mfd_shrinkable(const char *filename, const int lineno, int fd)
407 {
408 int fd2;
409
410 check_ftruncate(filename, lineno, fd, MFD_DEF_SIZE / 2);
411 check_mfd_size(filename, lineno, fd, MFD_DEF_SIZE / 2);
412
413 fd2 = check_mfd_open(filename, lineno, fd,
414 O_RDWR | O_CREAT | O_TRUNC, 0600);
415 safe_close(filename, lineno, NULL, fd2);
416
417 check_mfd_size(filename, lineno, fd, 0);
418 }
419
check_mfd_non_shrinkable(const char * filename,const int lineno,int fd)420 void check_mfd_non_shrinkable(const char *filename, const int lineno, int fd)
421 {
422 check_ftruncate_fail(filename, lineno, fd, MFD_DEF_SIZE / 2);
423 check_mfd_fail_open(filename, lineno, fd,
424 O_RDWR | O_CREAT | O_TRUNC, 0600);
425 }
426
check_mfd_growable(const char * filename,const int lineno,int fd)427 void check_mfd_growable(const char *filename, const int lineno, int fd)
428 {
429 check_ftruncate(filename, lineno, fd, MFD_DEF_SIZE * 2);
430 check_mfd_size(filename, lineno, fd, MFD_DEF_SIZE * 2);
431
432 check_fallocate(filename, lineno, fd, 0, 0, MFD_DEF_SIZE * 4);
433 check_mfd_size(filename, lineno, fd, MFD_DEF_SIZE * 4);
434 }
435
check_mfd_non_growable(const char * filename,const int lineno,int fd)436 void check_mfd_non_growable(const char *filename, const int lineno, int fd)
437 {
438 check_ftruncate_fail(filename, lineno, fd, MFD_DEF_SIZE * 2);
439 check_fallocate_fail(filename, lineno, fd, 0, 0, MFD_DEF_SIZE * 4);
440 }
441
check_mfd_growable_by_write(const char * filename,const int lineno,int fd)442 void check_mfd_growable_by_write(const char *filename, const int lineno, int fd)
443 {
444 char buf[MFD_DEF_SIZE * 8];
445
446 if (pwrite(fd, buf, sizeof(buf), 0) != sizeof(buf)) {
447 tst_res_(filename, lineno, TFAIL | TERRNO,
448 "pwrite(%d, %s, %zu, %d) failed",
449 fd, buf, sizeof(buf), 0);
450
451 return;
452 }
453
454 tst_res_(filename, lineno, TPASS, "pwrite(%d, %s, %zu, %d) succeeded",
455 fd, buf, sizeof(buf), 0);
456
457 check_mfd_size(filename, lineno, fd, MFD_DEF_SIZE * 8);
458 }
459
check_mfd_non_growable_by_write(const char * filename,const int lineno,int fd)460 void check_mfd_non_growable_by_write(const char *filename, const int lineno,
461 int fd)
462 {
463 char buf[MFD_DEF_SIZE * 8];
464
465 if (pwrite(fd, buf, sizeof(buf), 0) == sizeof(buf)) {
466 tst_res_(filename, lineno, TFAIL,
467 "pwrite(%d, %s, %zu, %d) didn't fail as expected",
468 fd, buf, sizeof(buf), 0);
469
470 return;
471 }
472
473 tst_res_(filename, lineno, TPASS, "pwrite(%d, %s, %zu, %d) succeeded",
474 fd, buf, sizeof(buf), 0);
475 }
476