xref: /freebsd/tests/sys/posixshm/memfd_test.c (revision 315ee00f)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2019 Kyle Evans <kevans@FreeBSD.org>
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27 
28 #include <sys/cdefs.h>
29 #include <sys/fcntl.h>
30 #include <sys/mman.h>
31 #include <sys/stat.h>
32 
33 #include <atf-c.h>
34 #include <errno.h>
35 #include <unistd.h>
36 
37 ATF_TC_WITHOUT_HEAD(basic);
38 ATF_TC_BODY(basic, tc)
39 {
40 	struct stat sb;
41 	int fd;
42 	char buf[8];
43 
44 	ATF_REQUIRE((fd = memfd_create("...", 0)) != -1);
45 
46 	/* write(2) should grow us out automatically. */
47 	ATF_REQUIRE(write(fd, buf, sizeof(buf)) == sizeof(buf));
48 	ATF_REQUIRE(fstat(fd, &sb) == 0);
49 	ATF_REQUIRE(sb.st_size == sizeof(buf));
50 
51 	/* ftruncate(2) must succeed without seals */
52 	ATF_REQUIRE(ftruncate(fd, 2 * (sizeof(buf) - 1)) == 0);
53 
54 	/* write(2) again must not be limited by ftruncate(2) size. */
55 	ATF_REQUIRE(write(fd, buf, sizeof(buf)) == sizeof(buf));
56 
57 	/* Sanity check. */
58 	ATF_REQUIRE(fstat(fd, &sb) == 0);
59 	ATF_REQUIRE(sb.st_size == 2 * sizeof(buf));
60 
61 	close(fd);
62 }
63 
64 ATF_TC_WITHOUT_HEAD(cloexec);
65 ATF_TC_BODY(cloexec, tc)
66 {
67 	int fd_nocl, fd_cl;
68 
69 	ATF_REQUIRE((fd_nocl = memfd_create("...", 0)) != -1);
70 	ATF_REQUIRE((fd_cl = memfd_create("...", MFD_CLOEXEC)) != -1);
71 
72 	ATF_REQUIRE((fcntl(fd_nocl, F_GETFD) & FD_CLOEXEC) == 0);
73 	ATF_REQUIRE((fcntl(fd_cl, F_GETFD) & FD_CLOEXEC) != 0);
74 
75 	close(fd_nocl);
76 	close(fd_cl);
77 }
78 
79 ATF_TC_WITHOUT_HEAD(disallowed_sealing);
80 ATF_TC_BODY(disallowed_sealing, tc)
81 {
82 	int fd;
83 
84 	ATF_REQUIRE((fd = memfd_create("...", 0)) != -1);
85 	ATF_REQUIRE(fcntl(fd, F_GET_SEALS) == F_SEAL_SEAL);
86 	ATF_REQUIRE(fcntl(fd, F_ADD_SEALS, F_SEAL_WRITE) == -1);
87 	ATF_REQUIRE(errno == EPERM);
88 
89 	close(fd);
90 }
91 
92 #define	BUF_SIZE	1024
93 
94 ATF_TC_WITHOUT_HEAD(write_seal);
95 ATF_TC_BODY(write_seal, tc)
96 {
97 	int fd;
98 	char *addr, buf[BUF_SIZE];
99 
100 	ATF_REQUIRE((fd = memfd_create("...", MFD_ALLOW_SEALING)) != -1);
101 	ATF_REQUIRE(ftruncate(fd, BUF_SIZE) == 0);
102 
103 	/* Write once, then we'll seal it and try again */
104 	ATF_REQUIRE(write(fd, buf, BUF_SIZE) == BUF_SIZE);
105 	ATF_REQUIRE(lseek(fd, 0, SEEK_SET) == 0);
106 
107 	addr = mmap(0, BUF_SIZE, (PROT_READ | PROT_WRITE), MAP_PRIVATE, fd, 0);
108 	ATF_REQUIRE(addr != MAP_FAILED);
109 	ATF_REQUIRE(munmap(addr, BUF_SIZE) == 0);
110 
111 	ATF_REQUIRE(fcntl(fd, F_ADD_SEALS, F_SEAL_WRITE) == 0);
112 
113 	ATF_REQUIRE(write(fd, buf, BUF_SIZE) == -1);
114 	ATF_REQUIRE(errno == EPERM);
115 
116 	ATF_REQUIRE(mmap(0, BUF_SIZE, (PROT_READ | PROT_WRITE), MAP_SHARED,
117 	    fd, 0) == MAP_FAILED);
118 	ATF_REQUIRE(errno == EACCES);
119 
120 	close(fd);
121 }
122 
123 ATF_TC_WITHOUT_HEAD(mmap_write_seal);
124 ATF_TC_BODY(mmap_write_seal, tc)
125 {
126 	int fd;
127 	char *addr, *paddr, *raddr;
128 
129 	ATF_REQUIRE((fd = memfd_create("...", MFD_ALLOW_SEALING)) != -1);
130 	ATF_REQUIRE(ftruncate(fd, BUF_SIZE) == 0);
131 
132 	/* Map it, both shared and privately */
133 	addr = mmap(0, BUF_SIZE, (PROT_READ | PROT_WRITE), MAP_SHARED, fd, 0);
134 	ATF_REQUIRE(addr != MAP_FAILED);
135 	paddr = mmap(0, BUF_SIZE, (PROT_READ | PROT_WRITE), MAP_PRIVATE, fd, 0);
136 	ATF_REQUIRE(paddr != MAP_FAILED);
137 	raddr = mmap(0, BUF_SIZE, PROT_READ, MAP_SHARED, fd, 0);
138 	ATF_REQUIRE(raddr != MAP_FAILED);
139 
140 	/* Now try to seal it before unmapping */
141 	ATF_REQUIRE(fcntl(fd, F_ADD_SEALS, F_SEAL_WRITE) == -1);
142 	ATF_REQUIRE(errno == EBUSY);
143 
144 	ATF_REQUIRE(munmap(addr, BUF_SIZE) == 0);
145 
146 	/*
147 	 * This should fail, because raddr still exists and it was spawned from
148 	 * a r/w fd.
149 	 */
150 	ATF_REQUIRE(fcntl(fd, F_ADD_SEALS, F_SEAL_WRITE) == -1);
151 	ATF_REQUIRE(errno == EBUSY);
152 
153 	ATF_REQUIRE(munmap(raddr, BUF_SIZE) == 0);
154 	/* This one should succeed; only the private mapping remains. */
155 	ATF_REQUIRE(fcntl(fd, F_ADD_SEALS, F_SEAL_WRITE) == 0);
156 
157 	ATF_REQUIRE(munmap(paddr, BUF_SIZE) == 0);
158 	ATF_REQUIRE(mmap(0, BUF_SIZE, (PROT_READ | PROT_WRITE), MAP_SHARED,
159 	    fd, 0) == MAP_FAILED);
160 	ATF_REQUIRE(errno == EACCES);
161 
162 	/* Make sure we can still map privately r/w or shared r/o. */
163 	paddr = mmap(0, BUF_SIZE, (PROT_READ | PROT_WRITE), MAP_PRIVATE, fd, 0);
164 	ATF_REQUIRE(paddr != MAP_FAILED);
165 	raddr = mmap(0, BUF_SIZE, PROT_READ, MAP_SHARED, fd, 0);
166 	ATF_REQUIRE(raddr != MAP_FAILED);
167 	ATF_REQUIRE(munmap(raddr, BUF_SIZE) == 0);
168 	ATF_REQUIRE(munmap(paddr, BUF_SIZE) == 0);
169 
170 	close(fd);
171 }
172 
173 static int
174 memfd_truncate_test(int initial_size, int dest_size, int seals)
175 {
176 	int err, fd;
177 
178 	ATF_REQUIRE((fd = memfd_create("...", MFD_ALLOW_SEALING)) != -1);
179 	ATF_REQUIRE(ftruncate(fd, initial_size) == 0);
180 
181 	ATF_REQUIRE(fcntl(fd, F_ADD_SEALS, seals) == 0);
182 
183 	err = ftruncate(fd, dest_size);
184 	if (err != 0)
185 		err = errno;
186 	close(fd);
187 	return (err);
188 }
189 
190 ATF_TC_WITHOUT_HEAD(truncate_seals);
191 ATF_TC_BODY(truncate_seals, tc)
192 {
193 
194 	ATF_REQUIRE(memfd_truncate_test(4, 8, F_SEAL_GROW) == EPERM);
195 	ATF_REQUIRE(memfd_truncate_test(8, 4, F_SEAL_SHRINK) == EPERM);
196 	ATF_REQUIRE(memfd_truncate_test(8, 4, F_SEAL_GROW) == 0);
197 	ATF_REQUIRE(memfd_truncate_test(4, 8, F_SEAL_SHRINK) == 0);
198 
199 	ATF_REQUIRE(memfd_truncate_test(4, 8, F_SEAL_GROW | F_SEAL_SHRINK) ==
200 	    EPERM);
201 	ATF_REQUIRE(memfd_truncate_test(8, 4, F_SEAL_GROW | F_SEAL_SHRINK) ==
202 	    EPERM);
203 	ATF_REQUIRE(memfd_truncate_test(4, 4, F_SEAL_GROW | F_SEAL_SHRINK) ==
204 	    0);
205 }
206 
207 ATF_TC_WITHOUT_HEAD(get_seals);
208 ATF_TC_BODY(get_seals, tc)
209 {
210 	int fd;
211 	int seals;
212 
213 	ATF_REQUIRE((fd = memfd_create("...", MFD_ALLOW_SEALING)) != -1);
214 	ATF_REQUIRE(fcntl(fd, F_GET_SEALS) == 0);
215 
216 	ATF_REQUIRE(fcntl(fd, F_ADD_SEALS, F_SEAL_WRITE | F_SEAL_GROW) == 0);
217 	seals = fcntl(fd, F_GET_SEALS);
218 	ATF_REQUIRE(seals == (F_SEAL_WRITE | F_SEAL_GROW));
219 
220 	close(fd);
221 }
222 
223 ATF_TC_WITHOUT_HEAD(dup_seals);
224 ATF_TC_BODY(dup_seals, tc)
225 {
226 	char buf[8];
227 	int fd, fdx;
228 	int seals;
229 
230 	ATF_REQUIRE((fd = memfd_create("...", MFD_ALLOW_SEALING)) != -1);
231 	ATF_REQUIRE((fdx = dup(fd)) != -1);
232 	ATF_REQUIRE(fcntl(fd, F_GET_SEALS) == 0);
233 
234 	ATF_REQUIRE(fcntl(fd, F_ADD_SEALS, F_SEAL_WRITE | F_SEAL_GROW) == 0);
235 	seals = fcntl(fd, F_GET_SEALS);
236 	ATF_REQUIRE(seals == (F_SEAL_WRITE | F_SEAL_GROW));
237 
238 	seals = fcntl(fdx, F_GET_SEALS);
239 	ATF_REQUIRE(seals == (F_SEAL_WRITE | F_SEAL_GROW));
240 
241 	/* Make sure the seal's actually being applied at the inode level */
242 	ATF_REQUIRE(write(fdx, buf, sizeof(buf)) == -1);
243 	ATF_REQUIRE(errno == EPERM);
244 
245 	ATF_REQUIRE(mmap(0, BUF_SIZE, (PROT_READ | PROT_WRITE), MAP_SHARED,
246 	    fdx, 0) == MAP_FAILED);
247 	ATF_REQUIRE(errno == EACCES);
248 
249 	close(fd);
250 	close(fdx);
251 }
252 
253 ATF_TC_WITHOUT_HEAD(immutable_seals);
254 ATF_TC_BODY(immutable_seals, tc)
255 {
256 	int fd;
257 
258 	ATF_REQUIRE((fd = memfd_create("...", MFD_ALLOW_SEALING)) != -1);
259 
260 	ATF_REQUIRE(fcntl(fd, F_ADD_SEALS, F_SEAL_SEAL) == 0);
261 	ATF_REQUIRE(fcntl(fd, F_ADD_SEALS, F_SEAL_GROW) == -1);
262 	ATF_REQUIRE_MSG(errno == EPERM,
263 	    "Added unique grow seal after restricting seals");
264 
265 	close(fd);
266 
267 	/*
268 	 * Also check that adding a seal that already exists really doesn't
269 	 * do anything once we're sealed.
270 	 */
271 	ATF_REQUIRE((fd = memfd_create("...", MFD_ALLOW_SEALING)) != -1);
272 
273 	ATF_REQUIRE(fcntl(fd, F_ADD_SEALS, F_SEAL_GROW | F_SEAL_SEAL) == 0);
274 	ATF_REQUIRE(fcntl(fd, F_ADD_SEALS, F_SEAL_GROW) == -1);
275 	ATF_REQUIRE_MSG(errno == EPERM,
276 	    "Added duplicate grow seal after restricting seals");
277 	close(fd);
278 }
279 
280 ATF_TP_ADD_TCS(tp)
281 {
282 
283 	ATF_TP_ADD_TC(tp, basic);
284 	ATF_TP_ADD_TC(tp, cloexec);
285 	ATF_TP_ADD_TC(tp, disallowed_sealing);
286 	ATF_TP_ADD_TC(tp, write_seal);
287 	ATF_TP_ADD_TC(tp, mmap_write_seal);
288 	ATF_TP_ADD_TC(tp, truncate_seals);
289 	ATF_TP_ADD_TC(tp, get_seals);
290 	ATF_TP_ADD_TC(tp, dup_seals);
291 	ATF_TP_ADD_TC(tp, immutable_seals);
292 	return (atf_no_error());
293 }
294