• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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