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