1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
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__FBSDID("$FreeBSD$");
30
31#include <sys/fcntl.h>
32#include <sys/mman.h>
33#include <sys/stat.h>
34
35#include <atf-c.h>
36#include <errno.h>
37#include <unistd.h>
38
39ATF_TC_WITHOUT_HEAD(basic);
40ATF_TC_BODY(basic, tc)
41{
42	struct stat sb;
43	int fd;
44	char buf[8];
45
46	ATF_REQUIRE((fd = memfd_create("...", 0)) != -1);
47
48	/* write(2) should grow us out automatically. */
49	ATF_REQUIRE(write(fd, buf, sizeof(buf)) == sizeof(buf));
50	ATF_REQUIRE(fstat(fd, &sb) == 0);
51	ATF_REQUIRE(sb.st_size == sizeof(buf));
52
53	/* ftruncate(2) must succeed without seals */
54	ATF_REQUIRE(ftruncate(fd, 2 * (sizeof(buf) - 1)) == 0);
55
56	/* write(2) again must not be limited by ftruncate(2) size. */
57	ATF_REQUIRE(write(fd, buf, sizeof(buf)) == sizeof(buf));
58
59	/* Sanity check. */
60	ATF_REQUIRE(fstat(fd, &sb) == 0);
61	ATF_REQUIRE(sb.st_size == 2 * sizeof(buf));
62
63	close(fd);
64}
65
66ATF_TC_WITHOUT_HEAD(cloexec);
67ATF_TC_BODY(cloexec, tc)
68{
69	int fd_nocl, fd_cl;
70
71	ATF_REQUIRE((fd_nocl = memfd_create("...", 0)) != -1);
72	ATF_REQUIRE((fd_cl = memfd_create("...", MFD_CLOEXEC)) != -1);
73
74	ATF_REQUIRE((fcntl(fd_nocl, F_GETFD) & FD_CLOEXEC) == 0);
75	ATF_REQUIRE((fcntl(fd_cl, F_GETFD) & FD_CLOEXEC) != 0);
76
77	close(fd_nocl);
78	close(fd_cl);
79}
80
81ATF_TC_WITHOUT_HEAD(disallowed_sealing);
82ATF_TC_BODY(disallowed_sealing, tc)
83{
84	int fd;
85
86	ATF_REQUIRE((fd = memfd_create("...", 0)) != -1);
87	ATF_REQUIRE(fcntl(fd, F_GET_SEALS) == F_SEAL_SEAL);
88	ATF_REQUIRE(fcntl(fd, F_ADD_SEALS, F_SEAL_WRITE) == -1);
89	ATF_REQUIRE(errno == EPERM);
90
91	close(fd);
92}
93
94#define	BUF_SIZE	1024
95
96ATF_TC_WITHOUT_HEAD(write_seal);
97ATF_TC_BODY(write_seal, tc)
98{
99	int fd;
100	char *addr, buf[BUF_SIZE];
101
102	ATF_REQUIRE((fd = memfd_create("...", MFD_ALLOW_SEALING)) != -1);
103	ATF_REQUIRE(ftruncate(fd, BUF_SIZE) == 0);
104
105	/* Write once, then we'll seal it and try again */
106	ATF_REQUIRE(write(fd, buf, BUF_SIZE) == BUF_SIZE);
107	ATF_REQUIRE(lseek(fd, 0, SEEK_SET) == 0);
108
109	addr = mmap(0, BUF_SIZE, (PROT_READ | PROT_WRITE), MAP_PRIVATE, fd, 0);
110	ATF_REQUIRE(addr != MAP_FAILED);
111	ATF_REQUIRE(munmap(addr, BUF_SIZE) == 0);
112
113	ATF_REQUIRE(fcntl(fd, F_ADD_SEALS, F_SEAL_WRITE) == 0);
114
115	ATF_REQUIRE(write(fd, buf, BUF_SIZE) == -1);
116	ATF_REQUIRE(errno == EPERM);
117
118	ATF_REQUIRE(mmap(0, BUF_SIZE, (PROT_READ | PROT_WRITE), MAP_SHARED,
119	    fd, 0) == MAP_FAILED);
120	ATF_REQUIRE(errno == EACCES);
121
122	close(fd);
123}
124
125ATF_TC_WITHOUT_HEAD(mmap_write_seal);
126ATF_TC_BODY(mmap_write_seal, tc)
127{
128	int fd;
129	char *addr, *paddr, *raddr;
130
131	ATF_REQUIRE((fd = memfd_create("...", MFD_ALLOW_SEALING)) != -1);
132	ATF_REQUIRE(ftruncate(fd, BUF_SIZE) == 0);
133
134	/* Map it, both shared and privately */
135	addr = mmap(0, BUF_SIZE, (PROT_READ | PROT_WRITE), MAP_SHARED, fd, 0);
136	ATF_REQUIRE(addr != MAP_FAILED);
137	paddr = mmap(0, BUF_SIZE, (PROT_READ | PROT_WRITE), MAP_PRIVATE, fd, 0);
138	ATF_REQUIRE(paddr != MAP_FAILED);
139	raddr = mmap(0, BUF_SIZE, PROT_READ, MAP_SHARED, fd, 0);
140	ATF_REQUIRE(raddr != MAP_FAILED);
141
142	/* Now try to seal it before unmapping */
143	ATF_REQUIRE(fcntl(fd, F_ADD_SEALS, F_SEAL_WRITE) == -1);
144	ATF_REQUIRE(errno == EBUSY);
145
146	ATF_REQUIRE(munmap(addr, BUF_SIZE) == 0);
147
148	/*
149	 * This should fail, because raddr still exists and it was spawned from
150	 * a r/w fd.
151	 */
152	ATF_REQUIRE(fcntl(fd, F_ADD_SEALS, F_SEAL_WRITE) == -1);
153	ATF_REQUIRE(errno == EBUSY);
154
155	ATF_REQUIRE(munmap(raddr, BUF_SIZE) == 0);
156	/* This one should succeed; only the private mapping remains. */
157	ATF_REQUIRE(fcntl(fd, F_ADD_SEALS, F_SEAL_WRITE) == 0);
158
159	ATF_REQUIRE(munmap(paddr, BUF_SIZE) == 0);
160	ATF_REQUIRE(mmap(0, BUF_SIZE, (PROT_READ | PROT_WRITE), MAP_SHARED,
161	    fd, 0) == MAP_FAILED);
162	ATF_REQUIRE(errno == EACCES);
163
164	/* Make sure we can still map privately r/w or shared r/o. */
165	paddr = mmap(0, BUF_SIZE, (PROT_READ | PROT_WRITE), MAP_PRIVATE, fd, 0);
166	ATF_REQUIRE(paddr != MAP_FAILED);
167	raddr = mmap(0, BUF_SIZE, PROT_READ, MAP_SHARED, fd, 0);
168	ATF_REQUIRE(raddr != MAP_FAILED);
169	ATF_REQUIRE(munmap(raddr, BUF_SIZE) == 0);
170	ATF_REQUIRE(munmap(paddr, BUF_SIZE) == 0);
171
172	close(fd);
173}
174
175static int
176memfd_truncate_test(int initial_size, int dest_size, int seals)
177{
178	int err, fd;
179
180	ATF_REQUIRE((fd = memfd_create("...", MFD_ALLOW_SEALING)) != -1);
181	ATF_REQUIRE(ftruncate(fd, initial_size) == 0);
182
183	ATF_REQUIRE(fcntl(fd, F_ADD_SEALS, seals) == 0);
184
185	err = ftruncate(fd, dest_size);
186	if (err != 0)
187		err = errno;
188	close(fd);
189	return (err);
190}
191
192ATF_TC_WITHOUT_HEAD(truncate_seals);
193ATF_TC_BODY(truncate_seals, tc)
194{
195
196	ATF_REQUIRE(memfd_truncate_test(4, 8, F_SEAL_GROW) == EPERM);
197	ATF_REQUIRE(memfd_truncate_test(8, 4, F_SEAL_SHRINK) == EPERM);
198	ATF_REQUIRE(memfd_truncate_test(8, 4, F_SEAL_GROW) == 0);
199	ATF_REQUIRE(memfd_truncate_test(4, 8, F_SEAL_SHRINK) == 0);
200
201	ATF_REQUIRE(memfd_truncate_test(4, 8, F_SEAL_GROW | F_SEAL_SHRINK) ==
202	    EPERM);
203	ATF_REQUIRE(memfd_truncate_test(8, 4, F_SEAL_GROW | F_SEAL_SHRINK) ==
204	    EPERM);
205	ATF_REQUIRE(memfd_truncate_test(4, 4, F_SEAL_GROW | F_SEAL_SHRINK) ==
206	    0);
207}
208
209ATF_TC_WITHOUT_HEAD(get_seals);
210ATF_TC_BODY(get_seals, tc)
211{
212	int fd;
213	int seals;
214
215	ATF_REQUIRE((fd = memfd_create("...", MFD_ALLOW_SEALING)) != -1);
216	ATF_REQUIRE(fcntl(fd, F_GET_SEALS) == 0);
217
218	ATF_REQUIRE(fcntl(fd, F_ADD_SEALS, F_SEAL_WRITE | F_SEAL_GROW) == 0);
219	seals = fcntl(fd, F_GET_SEALS);
220	ATF_REQUIRE(seals == (F_SEAL_WRITE | F_SEAL_GROW));
221
222	close(fd);
223}
224
225ATF_TC_WITHOUT_HEAD(dup_seals);
226ATF_TC_BODY(dup_seals, tc)
227{
228	char buf[8];
229	int fd, fdx;
230	int seals;
231
232	ATF_REQUIRE((fd = memfd_create("...", MFD_ALLOW_SEALING)) != -1);
233	ATF_REQUIRE((fdx = dup(fd)) != -1);
234	ATF_REQUIRE(fcntl(fd, F_GET_SEALS) == 0);
235
236	ATF_REQUIRE(fcntl(fd, F_ADD_SEALS, F_SEAL_WRITE | F_SEAL_GROW) == 0);
237	seals = fcntl(fd, F_GET_SEALS);
238	ATF_REQUIRE(seals == (F_SEAL_WRITE | F_SEAL_GROW));
239
240	seals = fcntl(fdx, F_GET_SEALS);
241	ATF_REQUIRE(seals == (F_SEAL_WRITE | F_SEAL_GROW));
242
243	/* Make sure the seal's actually being applied at the inode level */
244	ATF_REQUIRE(write(fdx, buf, sizeof(buf)) == -1);
245	ATF_REQUIRE(errno == EPERM);
246
247	ATF_REQUIRE(mmap(0, BUF_SIZE, (PROT_READ | PROT_WRITE), MAP_SHARED,
248	    fdx, 0) == MAP_FAILED);
249	ATF_REQUIRE(errno == EACCES);
250
251	close(fd);
252	close(fdx);
253}
254
255ATF_TC_WITHOUT_HEAD(immutable_seals);
256ATF_TC_BODY(immutable_seals, tc)
257{
258	int fd;
259
260	ATF_REQUIRE((fd = memfd_create("...", MFD_ALLOW_SEALING)) != -1);
261
262	ATF_REQUIRE(fcntl(fd, F_ADD_SEALS, F_SEAL_SEAL) == 0);
263	ATF_REQUIRE(fcntl(fd, F_ADD_SEALS, F_SEAL_GROW) == -1);
264	ATF_REQUIRE_MSG(errno == EPERM,
265	    "Added unique grow seal after restricting seals");
266
267	close(fd);
268
269	/*
270	 * Also check that adding a seal that already exists really doesn't
271	 * do anything once we're sealed.
272	 */
273	ATF_REQUIRE((fd = memfd_create("...", MFD_ALLOW_SEALING)) != -1);
274
275	ATF_REQUIRE(fcntl(fd, F_ADD_SEALS, F_SEAL_GROW | F_SEAL_SEAL) == 0);
276	ATF_REQUIRE(fcntl(fd, F_ADD_SEALS, F_SEAL_GROW) == -1);
277	ATF_REQUIRE_MSG(errno == EPERM,
278	    "Added duplicate grow seal after restricting seals");
279	close(fd);
280}
281
282ATF_TP_ADD_TCS(tp)
283{
284
285	ATF_TP_ADD_TC(tp, basic);
286	ATF_TP_ADD_TC(tp, cloexec);
287	ATF_TP_ADD_TC(tp, disallowed_sealing);
288	ATF_TP_ADD_TC(tp, write_seal);
289	ATF_TP_ADD_TC(tp, mmap_write_seal);
290	ATF_TP_ADD_TC(tp, truncate_seals);
291	ATF_TP_ADD_TC(tp, get_seals);
292	ATF_TP_ADD_TC(tp, dup_seals);
293	ATF_TP_ADD_TC(tp, immutable_seals);
294	return (atf_no_error());
295}
296