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