Home | History | Annotate | Line # | Download | only in kernel
      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