]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - tests/sys/kern/memfd_test.c
MFV 364467:
[FreeBSD/FreeBSD.git] / tests / sys / kern / memfd_test.c
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
39 ATF_TC_WITHOUT_HEAD(basic);
40 ATF_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
66 ATF_TC_WITHOUT_HEAD(cloexec);
67 ATF_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
81 ATF_TC_WITHOUT_HEAD(disallowed_sealing);
82 ATF_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
96 ATF_TC_WITHOUT_HEAD(write_seal);
97 ATF_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
125 ATF_TC_WITHOUT_HEAD(mmap_write_seal);
126 ATF_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
175 static int
176 memfd_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
192 ATF_TC_WITHOUT_HEAD(truncate_seals);
193 ATF_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
209 ATF_TC_WITHOUT_HEAD(get_seals);
210 ATF_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
225 ATF_TC_WITHOUT_HEAD(dup_seals);
226 ATF_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
255 ATF_TC_WITHOUT_HEAD(immutable_seals);
256 ATF_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
282 ATF_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 }