1 /* $NetBSD: t_memfd_create.c,v 1.7 2025/04/19 01:56:50 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.7 2025/04/19 01:56:50 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; 47 char *read_buf; 48 size_t rwbuf_size; 49 50 static void 51 setupbufs(void) 52 { 53 54 rwbuf_size = (size_t)sysconf(_SC_PAGESIZE); 55 ATF_REQUIRE_MSG(rwbuf_size < SIZE_MAX/2, "rwbuf_size=%zu", rwbuf_size); 56 rwbuf_size *= 2; 57 REQUIRE_LIBC(write_buf = calloc(1, rwbuf_size), NULL); 58 REQUIRE_LIBC(read_buf = calloc(1, rwbuf_size), NULL); 59 } 60 61 ATF_TC(create_null_name); 62 ATF_TC_HEAD(create_null_name, tc) 63 { 64 65 atf_tc_set_md_var(tc, "descr", 66 "Checks memfd_create fails with EFAULT when invalid memory" 67 " is provided"); 68 } 69 ATF_TC_BODY(create_null_name, tc) 70 { 71 int fd; 72 73 ATF_REQUIRE_EQ_MSG(fd = memfd_create(NULL, 0), -1, 74 "Unexpected success"); 75 ATF_REQUIRE_ERRNO(EFAULT, true); 76 } 77 78 ATF_TC(create_long_name); 79 ATF_TC_HEAD(create_long_name, tc) 80 { 81 82 atf_tc_set_md_var(tc, "descr", 83 "Checks memfd_create fails for names longer than NAME_MAX-6"); 84 } 85 ATF_TC_BODY(create_long_name, tc) 86 { 87 int fd; 88 89 memset(name_buf, 'A', sizeof(name_buf)); 90 name_buf[NAME_MAX-6] = '\0'; 91 92 ATF_REQUIRE_EQ_MSG(fd = memfd_create(name_buf, 0), -1, 93 "Unexpected success"); 94 ATF_REQUIRE_ERRNO(ENAMETOOLONG, true); 95 96 name_buf[NAME_MAX-7] = '\0'; 97 98 RL(fd = memfd_create(name_buf, 0)); 99 } 100 101 ATF_TC(read_write); 102 ATF_TC_HEAD(read_write, tc) 103 { 104 105 atf_tc_set_md_var(tc, "descr", 106 "Checks that data can be written to/read from a memfd"); 107 } 108 ATF_TC_BODY(read_write, tc) 109 { 110 int fd; 111 off_t offset; 112 113 setupbufs(); 114 115 RL(fd = memfd_create("", 0)); 116 117 tests_makegarbage(write_buf, rwbuf_size); 118 119 RL(write(fd, write_buf, rwbuf_size)); 120 offset = lseek(fd, 0, SEEK_CUR); 121 ATF_REQUIRE_EQ_MSG(offset, (off_t)rwbuf_size, 122 "File offset not set after write (%jd != %zu)", (intmax_t)offset, 123 rwbuf_size); 124 125 RL(lseek(fd, 0, SEEK_SET)); 126 127 RL(read(fd, read_buf, rwbuf_size)); 128 offset = lseek(fd, 0, SEEK_CUR); 129 ATF_REQUIRE_EQ_MSG(offset, (off_t)rwbuf_size, 130 "File offset not set after read (%jd != %zu)", (intmax_t)offset, 131 rwbuf_size); 132 133 for (size_t i = 0; i < rwbuf_size; i++) 134 ATF_REQUIRE_EQ_MSG(read_buf[i], write_buf[i], 135 "Data read does not match data written"); 136 } 137 138 ATF_TC(truncate); 139 ATF_TC_HEAD(truncate, tc) 140 { 141 142 atf_tc_set_md_var(tc, "descr", 143 "Checks that truncation does result in data removal"); 144 } 145 ATF_TC_BODY(truncate, tc) 146 { 147 int fd; 148 struct stat st; 149 150 setupbufs(); 151 152 RL(fd = memfd_create("", 0)); 153 154 tests_makegarbage(write_buf, rwbuf_size); 155 tests_makegarbage(read_buf, rwbuf_size); 156 157 RL(write(fd, write_buf, rwbuf_size)); 158 159 RL(fstat(fd, &st)); 160 ATF_REQUIRE_EQ_MSG(st.st_size, (off_t)rwbuf_size, 161 "Write did not grow size to %zu (is %jd)", rwbuf_size, 162 (intmax_t)st.st_size); 163 164 RL(ftruncate(fd, rwbuf_size/2)); 165 RL(fstat(fd, &st)); 166 ATF_REQUIRE_EQ_MSG(st.st_size, (off_t)rwbuf_size/2, 167 "Truncate did not shrink size to %zu (is %jd)", 168 rwbuf_size/2, (intmax_t)st.st_size); 169 170 RL(ftruncate(fd, rwbuf_size)); 171 RL(fstat(fd, &st)); 172 ATF_REQUIRE_EQ_MSG(st.st_size, (off_t)rwbuf_size, 173 "Truncate did not grow size to %zu (is %jd)", rwbuf_size, 174 (intmax_t)st.st_size); 175 176 RL(lseek(fd, 0, SEEK_SET)); 177 RL(read(fd, read_buf, rwbuf_size)); 178 179 for (size_t i = 0; i < rwbuf_size/2; i++) 180 ATF_REQUIRE_EQ_MSG(read_buf[i], write_buf[i], 181 "Data read does not match data written"); 182 for (size_t i = rwbuf_size/2; i < rwbuf_size; i++) 183 ATF_REQUIRE_EQ_MSG(read_buf[i], 0, 184 "Data read on growed region is not zeroed"); 185 } 186 187 ATF_TC(mmap); 188 ATF_TC_HEAD(mmap, tc) 189 { 190 191 atf_tc_set_md_var(tc, "descr", "Check that mmap succeeds"); 192 } 193 ATF_TC_BODY(mmap, tc) 194 { 195 int fd; 196 void *addr; 197 198 setupbufs(); 199 200 RL(fd = memfd_create("", 0)); 201 RL(ftruncate(fd, rwbuf_size)); 202 203 addr = mmap(NULL, rwbuf_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); 204 ATF_REQUIRE_MSG(addr != MAP_FAILED, 205 "mmap(NULL, %zu, 0x%x, 0x%x, %d, 0) failed: %s", 206 rwbuf_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 207 strerror(errno)); 208 } 209 210 ATF_TC(create_no_sealing); 211 ATF_TC_HEAD(create_no_sealing, tc) 212 { 213 214 atf_tc_set_md_var(tc, "descr", 215 "Checks that seals cannot be added if MFD_ALLOW_SEALING is" 216 " not specified to memfd_create"); 217 } 218 ATF_TC_BODY(create_no_sealing, tc) 219 { 220 int fd; 221 222 RL(fd = memfd_create("", 0)); 223 224 ATF_REQUIRE_EQ_MSG(fcntl(fd, F_ADD_SEALS, F_SEAL_WRITE), -1, 225 "fcntl succeeded unexpectedly"); 226 ATF_REQUIRE_ERRNO(EPERM, true); 227 } 228 229 ATF_TC(seal_seal); 230 ATF_TC_HEAD(seal_seal, tc) 231 { 232 233 atf_tc_set_md_var(tc, "descr", 234 "Checks adding F_SEAL_SEAL prevents adding other seals"); 235 } 236 ATF_TC_BODY(seal_seal, tc) 237 { 238 int fd; 239 240 RL(fd = memfd_create("", MFD_ALLOW_SEALING)); 241 RL(fcntl(fd, F_ADD_SEALS, F_SEAL_SEAL)); 242 243 ATF_REQUIRE_EQ_MSG(fcntl(fd, F_ADD_SEALS, F_SEAL_WRITE), -1, 244 "fcntl succeeded unexpectedly"); 245 ATF_REQUIRE_ERRNO(EPERM, true); 246 } 247 248 /* 249 * Tests that the seals provided in except to not also prevent some 250 * other operation. 251 * 252 * Note: fd must have a positive size. 253 */ 254 static void 255 test_all_seals_except(int fd, int except) 256 { 257 int rv; 258 struct stat st; 259 void *addr; 260 261 ATF_REQUIRE(rwbuf_size > 0); 262 263 RL(fstat(fd, &st)); 264 ATF_REQUIRE(st.st_size > 0); 265 266 if (except & ~F_SEAL_SEAL) { 267 rv = fcntl(fd, F_ADD_SEALS, F_SEAL_SEAL); 268 if (rv == -1) { 269 ATF_REQUIRE_MSG(errno != EPERM, 270 "Seal %x prevented F_ADD_SEALS", except); 271 ATF_REQUIRE_MSG(errno == EPERM, 272 "F_ADD_SEALS failed unexpectedly (%s)", 273 strerror(errno)); 274 } 275 } 276 277 if (except & ~(F_SEAL_WRITE|F_SEAL_FUTURE_WRITE)) { 278 RL(lseek(fd, 0, SEEK_SET)); 279 rv = write(fd, write_buf, rwbuf_size); 280 if (rv == -1) { 281 ATF_REQUIRE_MSG(errno != EPERM, 282 "Seal %x prevented write", except); 283 ATF_REQUIRE_MSG(errno == EPERM, 284 "Write failed unexpectedly (%s)", 285 strerror(errno)); 286 } 287 288 addr = mmap(NULL, st.st_size, PROT_READ|PROT_WRITE, MAP_SHARED, 289 fd, 0); 290 ATF_REQUIRE_MSG(addr != MAP_FAILED, 291 "mmap(NULL, %llu, 0x%x, 0x%x, %d, 0) failed: %s", 292 (unsigned long long)st.st_size, 293 PROT_READ|PROT_WRITE, MAP_SHARED, fd, 294 strerror(errno)); 295 } 296 297 if (except & ~F_SEAL_SHRINK) { 298 rv = ftruncate(fd, st.st_size - 1); 299 if (rv == -1) { 300 ATF_REQUIRE_MSG(errno != EPERM, 301 "Seal %x prevented truncate to shrink", except); 302 ATF_REQUIRE_MSG(errno == EPERM, 303 "Truncate failed unexpectedly (%s)", 304 strerror(errno)); 305 } 306 } 307 308 if (except & ~F_SEAL_GROW) { 309 rv = ftruncate(fd, st.st_size + 1); 310 if (rv == -1) { 311 ATF_REQUIRE_MSG(errno != EPERM, 312 "Seal %x prevented truncate to shrink", except); 313 ATF_REQUIRE_MSG(errno == EPERM, 314 "Truncate failed unexpectedly (%s)", 315 strerror(errno)); 316 } 317 } 318 } 319 320 ATF_TC(seal_shrink); 321 ATF_TC_HEAD(seal_shrink, tc) 322 { 323 324 atf_tc_set_md_var(tc, "descr", 325 "Checks F_SEAL_SHRINK prevents shrinking the file"); 326 } 327 ATF_TC_BODY(seal_shrink, tc) 328 { 329 int fd; 330 331 setupbufs(); 332 333 RL(fd = memfd_create("", MFD_ALLOW_SEALING)); 334 RL(ftruncate(fd, rwbuf_size)); 335 RL(fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK)); 336 337 ATF_REQUIRE_EQ_MSG(ftruncate(fd, rwbuf_size/2), -1, 338 "Truncate succeeded unexpectedly"); 339 ATF_REQUIRE_ERRNO(EPERM, true); 340 341 test_all_seals_except(fd, F_SEAL_SHRINK); 342 } 343 344 ATF_TC(seal_grow); 345 ATF_TC_HEAD(seal_grow, tc) 346 { 347 348 atf_tc_set_md_var(tc, "descr", 349 "Checks F_SEAL_SHRINK prevents growing the file"); 350 } 351 ATF_TC_BODY(seal_grow, tc) 352 { 353 int fd; 354 355 setupbufs(); 356 357 RL(fd = memfd_create("", MFD_ALLOW_SEALING)); 358 RL(ftruncate(fd, rwbuf_size/2)); 359 RL(fcntl(fd, F_ADD_SEALS, F_SEAL_GROW)); 360 361 ATF_REQUIRE_EQ_MSG(ftruncate(fd, rwbuf_size), -1, 362 "Truncate succeeded unexpectedly"); 363 ATF_REQUIRE_ERRNO(EPERM, true); 364 365 test_all_seals_except(fd, F_SEAL_GROW); 366 } 367 368 ATF_TC(seal_write); 369 ATF_TC_HEAD(seal_write, tc) 370 { 371 372 atf_tc_set_md_var(tc, "descr", 373 "Checks F_SEAL_WRITE prevents writing"); 374 } 375 ATF_TC_BODY(seal_write, tc) 376 { 377 int fd; 378 379 setupbufs(); 380 381 RL(fd = memfd_create("", MFD_ALLOW_SEALING)); 382 RL(ftruncate(fd, rwbuf_size/2)); 383 RL(fcntl(fd, F_ADD_SEALS, F_SEAL_WRITE)); 384 385 ATF_REQUIRE_EQ_MSG(write(fd, write_buf, rwbuf_size), -1, 386 "Write succeeded unexpectedly"); 387 ATF_REQUIRE_ERRNO(EPERM, true); 388 389 test_all_seals_except(fd, F_SEAL_WRITE); 390 } 391 392 ATF_TC(seal_write_mmap); 393 ATF_TC_HEAD(seal_write_mmap, tc) 394 { 395 396 atf_tc_set_md_var(tc, "descr", 397 "Checks that F_SEAL_WRITE cannot be added with open mmaps"); 398 } 399 ATF_TC_BODY(seal_write_mmap, tc) 400 { 401 int fd; 402 void *addr; 403 404 setupbufs(); 405 406 RL(fd = memfd_create("", MFD_ALLOW_SEALING)); 407 RL(ftruncate(fd, rwbuf_size)); 408 409 addr = mmap(NULL, rwbuf_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); 410 ATF_REQUIRE_MSG(addr != MAP_FAILED, 411 "mmap(NULL, %zu, 0x%x, 0x%x, %d, 0) failed: %s", 412 rwbuf_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 413 strerror(errno)); 414 415 ATF_REQUIRE_EQ_MSG(fcntl(fd, F_ADD_SEALS, F_SEAL_WRITE), -1, 416 "fcntl succeeded unexpectedly"); 417 ATF_REQUIRE_ERRNO(EBUSY, true); 418 } 419 420 ATF_TC(seal_future_write); 421 ATF_TC_HEAD(seal_future_write, tc) 422 { 423 424 atf_tc_set_md_var(tc, "descr", 425 "Checks F_SEAL_FUTURE_WRITE prevents writing"); 426 } 427 ATF_TC_BODY(seal_future_write, tc) 428 { 429 int fd; 430 431 setupbufs(); 432 433 RL(fd = memfd_create("", MFD_ALLOW_SEALING)); 434 RL(ftruncate(fd, rwbuf_size/2)); 435 RL(fcntl(fd, F_ADD_SEALS, F_SEAL_FUTURE_WRITE)); 436 437 ATF_REQUIRE_EQ_MSG(write(fd, write_buf, rwbuf_size), -1, 438 "Write succeeded unexpectedly"); 439 ATF_REQUIRE_ERRNO(EPERM, true); 440 441 test_all_seals_except(fd, F_SEAL_FUTURE_WRITE); 442 } 443 444 ATF_TC(seal_future_write_mmap); 445 ATF_TC_HEAD(seal_future_write_mmap, tc) 446 { 447 448 atf_tc_set_md_var(tc, "descr", 449 "Checks that F_SEAL_WRITE can be added with open mmaps but" 450 " prevents creating new ones"); 451 } 452 ATF_TC_BODY(seal_future_write_mmap, tc) 453 { 454 int fd; 455 void *addr; 456 457 setupbufs(); 458 459 RL(fd = memfd_create("", MFD_ALLOW_SEALING)); 460 RL(ftruncate(fd, rwbuf_size)); 461 addr = mmap(NULL, rwbuf_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); 462 ATF_REQUIRE_MSG(addr != MAP_FAILED, 463 "mmap(NULL, %zu, 0x%x, 0x%x, %d, 0) failed: %s", 464 rwbuf_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 465 strerror(errno)); 466 467 RL(fcntl(fd, F_ADD_SEALS, F_SEAL_FUTURE_WRITE)); 468 469 ATF_REQUIRE_EQ_MSG(mmap(NULL, rwbuf_size, PROT_READ|PROT_WRITE, 470 MAP_SHARED, fd, 0), MAP_FAILED, 471 "mmap(NULL, %zu, 0x%x, 0x%x, %d, 0) succeeded unexpectedly", 472 rwbuf_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd); 473 ATF_REQUIRE_ERRNO(EPERM, true); 474 } 475 476 477 ATF_TP_ADD_TCS(tp) 478 { 479 ATF_TP_ADD_TC(tp, create_null_name); 480 ATF_TP_ADD_TC(tp, create_long_name); 481 ATF_TP_ADD_TC(tp, read_write); 482 ATF_TP_ADD_TC(tp, truncate); 483 ATF_TP_ADD_TC(tp, mmap); 484 ATF_TP_ADD_TC(tp, create_no_sealing); 485 ATF_TP_ADD_TC(tp, seal_seal); 486 ATF_TP_ADD_TC(tp, seal_shrink); 487 ATF_TP_ADD_TC(tp, seal_grow); 488 ATF_TP_ADD_TC(tp, seal_write); 489 ATF_TP_ADD_TC(tp, seal_write_mmap); 490 ATF_TP_ADD_TC(tp, seal_future_write); 491 ATF_TP_ADD_TC(tp, seal_future_write_mmap); 492 493 return atf_no_error(); 494 } 495