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