t_memfd_create.c revision 1.4 1 /* $NetBSD: t_memfd_create.c,v 1.4 2025/04/17 16:02:48 riastradh Exp $ */
2
3 /*-
4 * Copyright (c) 2023 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Theodore Preduta.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31 #include <sys/cdefs.h>
32 __RCSID("$NetBSD: t_memfd_create.c,v 1.4 2025/04/17 16:02:48 riastradh Exp $");
33
34 #include <sys/param.h>
35 #include <sys/types.h>
36 #include <sys/mman.h>
37 #include <sys/stat.h>
38 #include <errno.h>
39 #include <fcntl.h>
40
41 #include <atf-c.h>
42
43 #include "h_macros.h"
44
45 char name_buf[NAME_MAX];
46 char write_buf[8192];
47 char read_buf[8192];
48
49 ATF_TC(create_null_name);
50 ATF_TC_HEAD(create_null_name, tc)
51 {
52
53 atf_tc_set_md_var(tc, "descr",
54 "Checks memfd_create fails with EFAULT when invalid memory"
55 " is provided");
56 }
57 ATF_TC_BODY(create_null_name, tc)
58 {
59 int fd;
60
61 ATF_REQUIRE_EQ_MSG(fd = memfd_create(NULL, 0), -1,
62 "Unexpected success");
63 ATF_REQUIRE_ERRNO(EFAULT, true);
64 }
65
66 ATF_TC(create_long_name);
67 ATF_TC_HEAD(create_long_name, tc)
68 {
69
70 atf_tc_set_md_var(tc, "descr",
71 "Checks memfd_create fails for names longer than NAME_MAX-6");
72 }
73 ATF_TC_BODY(create_long_name, tc)
74 {
75 int fd;
76
77 memset(name_buf, 'A', sizeof(name_buf));
78 name_buf[NAME_MAX-6] = '\0';
79
80 ATF_REQUIRE_EQ_MSG(fd = memfd_create(name_buf, 0), -1,
81 "Unexpected success");
82 ATF_REQUIRE_ERRNO(ENAMETOOLONG, true);
83
84 name_buf[NAME_MAX-7] = '\0';
85
86 RL(fd = memfd_create(name_buf, 0));
87 }
88
89 ATF_TC(read_write);
90 ATF_TC_HEAD(read_write, tc)
91 {
92
93 atf_tc_set_md_var(tc, "descr",
94 "Checks that data can be written to/read from a memfd");
95 }
96 ATF_TC_BODY(read_write, tc)
97 {
98 int fd;
99 off_t offset;
100
101 RL(fd = memfd_create("", 0));
102
103 tests_makegarbage(write_buf, sizeof(write_buf));
104 memset(read_buf, 0, sizeof(read_buf));
105
106 RL(write(fd, write_buf, sizeof(write_buf)));
107 offset = lseek(fd, 0, SEEK_CUR);
108 ATF_REQUIRE_EQ_MSG(offset, sizeof(write_buf),
109 "File offset not set after write (%jd != %zu)", (intmax_t)offset,
110 sizeof(write_buf));
111
112 RL(lseek(fd, 0, SEEK_SET));
113
114 RL(read(fd, read_buf, sizeof(read_buf)));
115 offset = lseek(fd, 0, SEEK_CUR);
116 ATF_REQUIRE_EQ_MSG(offset, sizeof(read_buf),
117 "File offset not set after read (%jd != %zu)", (intmax_t)offset,
118 sizeof(read_buf));
119
120 for (size_t i = 0; i < sizeof(read_buf); i++)
121 ATF_REQUIRE_EQ_MSG(read_buf[i], write_buf[i],
122 "Data read does not match data written");
123 }
124
125 ATF_TC(truncate);
126 ATF_TC_HEAD(truncate, tc)
127 {
128
129 atf_tc_set_md_var(tc, "descr",
130 "Checks that truncation does result in data removal");
131 }
132 ATF_TC_BODY(truncate, tc)
133 {
134 int fd;
135 struct stat st;
136
137 RL(fd = memfd_create("", 0));
138
139 tests_makegarbage(write_buf, sizeof(write_buf));
140 tests_makegarbage(read_buf, sizeof(read_buf));
141
142 RL(write(fd, write_buf, sizeof(write_buf)));
143
144 RL(fstat(fd, &st));
145 ATF_REQUIRE_EQ_MSG(st.st_size, sizeof(write_buf),
146 "Write did not grow size to %zu (is %jd)", sizeof(write_buf),
147 (intmax_t)st.st_size);
148
149 RL(ftruncate(fd, sizeof(write_buf)/2));
150 RL(fstat(fd, &st));
151 ATF_REQUIRE_EQ_MSG(st.st_size, sizeof(write_buf)/2,
152 "Truncate did not shrink size to %zu (is %jd)",
153 sizeof(write_buf)/2, (intmax_t)st.st_size);
154
155 RL(ftruncate(fd, sizeof(read_buf)));
156 RL(fstat(fd, &st));
157 ATF_REQUIRE_EQ_MSG(st.st_size, sizeof(read_buf),
158 "Truncate did not grow size to %zu (is %jd)", sizeof(read_buf),
159 (intmax_t)st.st_size);
160
161 RL(lseek(fd, 0, SEEK_SET));
162 RL(read(fd, read_buf, sizeof(read_buf)));
163
164 for (size_t i = 0; i < sizeof(read_buf)/2; i++)
165 ATF_REQUIRE_EQ_MSG(read_buf[i], write_buf[i],
166 "Data read does not match data written");
167 for (size_t i = sizeof(read_buf)/2; i < sizeof(read_buf); i++)
168 ATF_REQUIRE_EQ_MSG(read_buf[i], 0,
169 "Data read on growed region is not zeroed");
170 }
171
172 ATF_TC(mmap);
173 ATF_TC_HEAD(mmap, tc)
174 {
175
176 atf_tc_set_md_var(tc, "descr", "Check that mmap succeeds");
177 }
178 ATF_TC_BODY(mmap, tc)
179 {
180 int fd;
181 void *addr;
182
183 RL(fd = memfd_create("", 0));
184 RL(ftruncate(fd, sizeof(read_buf)));
185
186 addr = mmap(NULL, sizeof(read_buf), PROT_READ|PROT_WRITE, MAP_SHARED,
187 fd, 0);
188 ATF_REQUIRE_MSG(addr != MAP_FAILED,
189 "mmap(NULL, %zu, 0x%x, 0x%x, %d, 0) failed:"
190 " %s",
191 sizeof(read_buf), PROT_READ|PROT_WRITE, MAP_SHARED, fd,
192 strerror(errno));
193 }
194
195 ATF_TC(create_no_sealing);
196 ATF_TC_HEAD(create_no_sealing, tc)
197 {
198
199 atf_tc_set_md_var(tc, "descr",
200 "Checks that seals cannot be added if MFD_ALLOW_SEALING is"
201 " not specified to memfd_create");
202 }
203 ATF_TC_BODY(create_no_sealing, tc)
204 {
205 int fd;
206
207 RL(fd = memfd_create("", 0));
208
209 ATF_REQUIRE_EQ_MSG(fcntl(fd, F_ADD_SEALS, F_SEAL_WRITE), -1,
210 "fcntl succeeded unexpectedly");
211 ATF_REQUIRE_ERRNO(EPERM, true);
212 }
213
214 ATF_TC(seal_seal);
215 ATF_TC_HEAD(seal_seal, tc)
216 {
217
218 atf_tc_set_md_var(tc, "descr",
219 "Checks adding F_SEAL_SEAL prevents adding other seals");
220 }
221 ATF_TC_BODY(seal_seal, tc)
222 {
223 int fd;
224
225 RL(fd = memfd_create("", MFD_ALLOW_SEALING));
226 RL(fcntl(fd, F_ADD_SEALS, F_SEAL_SEAL));
227
228 ATF_REQUIRE_EQ_MSG(fcntl(fd, F_ADD_SEALS, F_SEAL_WRITE), -1,
229 "fcntl succeeded unexpectedly");
230 ATF_REQUIRE_ERRNO(EPERM, true);
231 }
232
233 /*
234 * Tests that the seals provided in except to not also prevent some
235 * other operation.
236 *
237 * Note: fd must have a positive size.
238 */
239 static void
240 test_all_seals_except(int fd, int except)
241 {
242 int rv;
243 struct stat st;
244 void *addr;
245
246 RL(fstat(fd, &st));
247 ATF_REQUIRE(st.st_size > 0);
248
249 if (except & ~F_SEAL_SEAL) {
250 rv = fcntl(fd, F_ADD_SEALS, F_SEAL_SEAL);
251 if (rv == -1) {
252 ATF_REQUIRE_MSG(errno != EPERM,
253 "Seal %x prevented F_ADD_SEALS", except);
254 ATF_REQUIRE_MSG(errno == EPERM,
255 "F_ADD_SEALS failed unexpectedly (%s)",
256 strerror(errno));
257 }
258 }
259
260 if (except & ~(F_SEAL_WRITE|F_SEAL_FUTURE_WRITE)) {
261 RL(lseek(fd, 0, SEEK_SET));
262 rv = write(fd, write_buf, sizeof(write_buf));
263 if (rv == -1) {
264 ATF_REQUIRE_MSG(errno != EPERM,
265 "Seal %x prevented write", except);
266 ATF_REQUIRE_MSG(errno == EPERM,
267 "Write failed unexpectedly (%s)",
268 strerror(errno));
269 }
270
271 addr = mmap(NULL, st.st_size, PROT_READ|PROT_WRITE,
272 MAP_SHARED, fd, 0);
273 ATF_REQUIRE_MSG(addr != MAP_FAILED,
274 "mmap(NULL, %llu, 0x%x, 0x%x, %d, 0) failed:"
275 " %s",
276 (unsigned long long)st.st_size,
277 PROT_READ|PROT_WRITE, MAP_SHARED, fd,
278 strerror(errno));
279 }
280
281 if (except & ~F_SEAL_SHRINK) {
282 rv = ftruncate(fd, st.st_size - 1);
283 if (rv == -1) {
284 ATF_REQUIRE_MSG(errno != EPERM,
285 "Seal %x prevented truncate to shrink", except);
286 ATF_REQUIRE_MSG(errno == EPERM,
287 "Truncate failed unexpectedly (%s)",
288 strerror(errno));
289 }
290 }
291
292 if (except & ~F_SEAL_GROW) {
293 rv = ftruncate(fd, st.st_size + 1);
294 if (rv == -1) {
295 ATF_REQUIRE_MSG(errno != EPERM,
296 "Seal %x prevented truncate to shrink", except);
297 ATF_REQUIRE_MSG(errno == EPERM,
298 "Truncate failed unexpectedly (%s)",
299 strerror(errno));
300 }
301 }
302 }
303
304 ATF_TC(seal_shrink);
305 ATF_TC_HEAD(seal_shrink, tc)
306 {
307
308 atf_tc_set_md_var(tc, "descr",
309 "Checks F_SEAL_SHRINK prevents shrinking the file");
310 }
311 ATF_TC_BODY(seal_shrink, tc)
312 {
313 int fd;
314
315 RL(fd = memfd_create("", MFD_ALLOW_SEALING));
316 RL(ftruncate(fd, sizeof(write_buf)));
317 RL(fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK));
318
319 ATF_REQUIRE_EQ_MSG(ftruncate(fd, sizeof(write_buf)/2), -1,
320 "Truncate succeeded unexpectedly");
321 ATF_REQUIRE_ERRNO(EPERM, true);
322
323 test_all_seals_except(fd, F_SEAL_SHRINK);
324 }
325
326 ATF_TC(seal_grow);
327 ATF_TC_HEAD(seal_grow, tc)
328 {
329
330 atf_tc_set_md_var(tc, "descr",
331 "Checks F_SEAL_SHRINK prevents growing the file");
332 }
333 ATF_TC_BODY(seal_grow, tc)
334 {
335 int fd;
336
337 RL(fd = memfd_create("", MFD_ALLOW_SEALING));
338 RL(ftruncate(fd, sizeof(write_buf)/2));
339 RL(fcntl(fd, F_ADD_SEALS, F_SEAL_GROW));
340
341 ATF_REQUIRE_EQ_MSG(ftruncate(fd, sizeof(write_buf)), -1,
342 "Truncate succeeded unexpectedly");
343 ATF_REQUIRE_ERRNO(EPERM, true);
344
345 test_all_seals_except(fd, F_SEAL_GROW);
346 }
347
348 ATF_TC(seal_write);
349 ATF_TC_HEAD(seal_write, tc)
350 {
351
352 atf_tc_set_md_var(tc, "descr",
353 "Checks F_SEAL_WRITE prevents writing");
354 }
355 ATF_TC_BODY(seal_write, tc)
356 {
357 int fd;
358
359 RL(fd = memfd_create("", MFD_ALLOW_SEALING));
360 RL(ftruncate(fd, sizeof(write_buf)/2));
361 RL(fcntl(fd, F_ADD_SEALS, F_SEAL_WRITE));
362
363 ATF_REQUIRE_EQ_MSG(write(fd, write_buf, sizeof(write_buf)), -1,
364 "Write succeeded unexpectedly");
365 ATF_REQUIRE_ERRNO(EPERM, true);
366
367 test_all_seals_except(fd, F_SEAL_WRITE);
368 }
369
370 ATF_TC(seal_write_mmap);
371 ATF_TC_HEAD(seal_write_mmap, tc)
372 {
373
374 atf_tc_set_md_var(tc, "descr",
375 "Checks that F_SEAL_WRITE cannot be added with open mmaps");
376 }
377 ATF_TC_BODY(seal_write_mmap, tc)
378 {
379 int fd;
380 void *addr;
381
382 RL(fd = memfd_create("", MFD_ALLOW_SEALING));
383 RL(ftruncate(fd, sizeof(read_buf)));
384
385 addr = mmap(NULL, sizeof(read_buf), PROT_READ|PROT_WRITE, MAP_SHARED,
386 fd, 0);
387 ATF_REQUIRE_MSG(addr != MAP_FAILED,
388 "mmap(NULL, %zu, 0x%x, 0x%x, %d, 0) failed:"
389 " %s",
390 sizeof(read_buf), PROT_READ|PROT_WRITE, MAP_SHARED, fd,
391 strerror(errno));
392
393 ATF_REQUIRE_EQ_MSG(fcntl(fd, F_ADD_SEALS, F_SEAL_WRITE), -1,
394 "fcntl succeeded unexpectedly");
395 ATF_REQUIRE_ERRNO(EBUSY, true);
396 }
397
398 ATF_TC(seal_future_write);
399 ATF_TC_HEAD(seal_future_write, tc)
400 {
401
402 atf_tc_set_md_var(tc, "descr",
403 "Checks F_SEAL_FUTURE_WRITE prevents writing");
404 }
405 ATF_TC_BODY(seal_future_write, tc)
406 {
407 int fd;
408
409 RL(fd = memfd_create("", MFD_ALLOW_SEALING));
410 RL(ftruncate(fd, sizeof(write_buf)/2));
411 RL(fcntl(fd, F_ADD_SEALS, F_SEAL_FUTURE_WRITE));
412
413 ATF_REQUIRE_EQ_MSG(write(fd, write_buf, sizeof(write_buf)), -1,
414 "Write succeeded unexpectedly");
415 ATF_REQUIRE_ERRNO(EPERM, true);
416
417 test_all_seals_except(fd, F_SEAL_FUTURE_WRITE);
418 }
419
420 ATF_TC(seal_future_write_mmap);
421 ATF_TC_HEAD(seal_future_write_mmap, tc)
422 {
423
424 atf_tc_set_md_var(tc, "descr",
425 "Checks that F_SEAL_WRITE can be added with open mmaps but"
426 " prevents creating new ones");
427 }
428 ATF_TC_BODY(seal_future_write_mmap, tc)
429 {
430 int fd;
431 void *addr;
432
433 RL(fd = memfd_create("", MFD_ALLOW_SEALING));
434 RL(ftruncate(fd, sizeof(read_buf)));
435 addr = mmap(NULL, sizeof(read_buf), PROT_READ|PROT_WRITE, MAP_SHARED,
436 fd, 0);
437 ATF_REQUIRE_MSG(addr != MAP_FAILED,
438 "mmap(NULL, %zu, 0x%x, 0x%x, %d, 0) failed:"
439 " %s",
440 sizeof(read_buf), PROT_READ|PROT_WRITE, MAP_SHARED, fd,
441 strerror(errno));
442
443 RL(fcntl(fd, F_ADD_SEALS, F_SEAL_FUTURE_WRITE));
444
445 ATF_REQUIRE_EQ_MSG(mmap(NULL, sizeof(read_buf), PROT_READ|PROT_WRITE,
446 MAP_SHARED, fd, 0), MAP_FAILED,
447 "mmap(NULL, %zu, 0x%x, 0x%x, %d, 0) succeeded unexpectedly",
448 sizeof(read_buf), PROT_READ|PROT_WRITE, MAP_SHARED, fd);
449 ATF_REQUIRE_ERRNO(EPERM, true);
450 }
451
452
453 ATF_TP_ADD_TCS(tp)
454 {
455 ATF_TP_ADD_TC(tp, create_null_name);
456 ATF_TP_ADD_TC(tp, create_long_name);
457 ATF_TP_ADD_TC(tp, read_write);
458 ATF_TP_ADD_TC(tp, truncate);
459 ATF_TP_ADD_TC(tp, mmap);
460 ATF_TP_ADD_TC(tp, create_no_sealing);
461 ATF_TP_ADD_TC(tp, seal_seal);
462 ATF_TP_ADD_TC(tp, seal_shrink);
463 ATF_TP_ADD_TC(tp, seal_grow);
464 ATF_TP_ADD_TC(tp, seal_write);
465 ATF_TP_ADD_TC(tp, seal_write_mmap);
466 ATF_TP_ADD_TC(tp, seal_future_write);
467 ATF_TP_ADD_TC(tp, seal_future_write_mmap);
468
469 return atf_no_error();
470 }
471