t_mmap.c revision 1.1 1 /* $NetBSD: t_mmap.c,v 1.1 2011/07/07 06:57:54 jruoho Exp $ */
2
3 /*-
4 * Copyright (c) 2011 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Jukka Ruohonen.
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_mmap.c,v 1.1 2011/07/07 06:57:54 jruoho Exp $");
33
34 #include <sys/param.h>
35 #include <sys/mman.h>
36 #include <sys/sysctl.h>
37 #include <sys/wait.h>
38
39 #include <errno.h>
40 #include <fcntl.h>
41 #include <signal.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <unistd.h>
45
46 #include <atf-c.h>
47
48 static long page = 0;
49 static char path[] = "mmap";
50 static void map_check(void *, int);
51 static void map_sighandler(int);
52
53 static void
54 map_check(void *map, int flag)
55 {
56
57 if (flag != 0) {
58 ATF_REQUIRE(map == MAP_FAILED);
59 return;
60 }
61
62 ATF_REQUIRE(map != MAP_FAILED);
63 ATF_REQUIRE(munmap(map, page) == 0);
64 }
65
66 static void
67 map_sighandler(int signo)
68 {
69 _exit(signo);
70 }
71
72 ATF_TC(mmap_err);
73 ATF_TC_HEAD(mmap_err, tc)
74 {
75 atf_tc_set_md_var(tc, "descr", "Test error conditions of mmap(2)");
76 }
77
78 ATF_TC_BODY(mmap_err, tc)
79 {
80 size_t addr = SIZE_MAX;
81 void *map;
82
83 errno = 0;
84 map = mmap(NULL, 3, PROT_READ, MAP_FILE|MAP_PRIVATE, -1, 0);
85
86 ATF_REQUIRE(map == MAP_FAILED);
87 ATF_REQUIRE(errno == EBADF);
88
89 errno = 0;
90 map = mmap(&addr, page, PROT_READ, MAP_FIXED|MAP_PRIVATE, -1, 0);
91
92 ATF_REQUIRE(map == MAP_FAILED);
93 ATF_REQUIRE(errno == EINVAL);
94
95 errno = 0;
96 map = mmap(NULL, page, PROT_READ, MAP_ANON|MAP_PRIVATE, INT_MAX, 0);
97
98 ATF_REQUIRE(map == MAP_FAILED);
99 ATF_REQUIRE(errno == EINVAL);
100 }
101
102 ATF_TC_WITH_CLEANUP(mmap_prot_1);
103 ATF_TC_HEAD(mmap_prot_1, tc)
104 {
105 atf_tc_set_md_var(tc, "descr", "Test mmap(2) protections, #1");
106 }
107
108 ATF_TC_BODY(mmap_prot_1, tc)
109 {
110 void *map;
111 int fd;
112
113 /*
114 * Open a file write-only and try to
115 * map it read-only. This should fail.
116 */
117 fd = open(path, O_WRONLY | O_CREAT, 0700);
118
119 if (fd < 0)
120 return;
121
122 ATF_REQUIRE(write(fd, "XXX", 3) == 3);
123
124 map = mmap(NULL, 3, PROT_READ, MAP_FILE|MAP_PRIVATE, fd, 0);
125 map_check(map, 1);
126
127 map = mmap(NULL, 3, PROT_WRITE, MAP_FILE|MAP_PRIVATE, fd, 0);
128 map_check(map, 0);
129
130 ATF_REQUIRE(close(fd) == 0);
131 }
132
133 ATF_TC_CLEANUP(mmap_prot_1, tc)
134 {
135 (void)unlink(path);
136 }
137
138 ATF_TC(mmap_prot_2);
139 ATF_TC_HEAD(mmap_prot_2, tc)
140 {
141 atf_tc_set_md_var(tc, "descr", "Test mmap(2) protections, #2");
142 }
143
144 ATF_TC_BODY(mmap_prot_2, tc)
145 {
146 char buf[2];
147 void *map;
148 pid_t pid;
149 int sta;
150
151 /*
152 * Make a PROT_NONE mapping and try to access it.
153 * If we catch a SIGSEGV, all works as expected.
154 */
155 map = mmap(NULL, page, PROT_NONE, MAP_ANON|MAP_PRIVATE, -1, 0);
156 ATF_REQUIRE(map != MAP_FAILED);
157
158 pid = fork();
159 ATF_REQUIRE(pid >= 0);
160
161 if (pid == 0) {
162 ATF_REQUIRE(signal(SIGSEGV, map_sighandler) != SIG_ERR);
163 ATF_REQUIRE(strlcpy(buf, map, sizeof(buf)) != 0);
164 }
165
166 (void)wait(&sta);
167
168 ATF_REQUIRE(WIFEXITED(sta) != 0);
169 ATF_REQUIRE(WEXITSTATUS(sta) == SIGSEGV);
170 ATF_REQUIRE(munmap(map, page) == 0);
171 }
172
173 ATF_TC_WITH_CLEANUP(mmap_prot_3);
174 ATF_TC_HEAD(mmap_prot_3, tc)
175 {
176 atf_tc_set_md_var(tc, "descr", "Test mmap(2) protections, #3");
177 }
178
179 ATF_TC_BODY(mmap_prot_3, tc)
180 {
181 char buf[2];
182 int fd, sta;
183 void *map;
184 pid_t pid;
185
186 /*
187 * Open a file, change the permissions
188 * to read-only, and try to map it as
189 * PROT_NONE. This should succeed, but
190 * the access should generate SIGSEGV.
191 */
192 fd = open(path, O_RDWR | O_CREAT, 0700);
193
194 if (fd < 0)
195 return;
196
197 ATF_REQUIRE(write(fd, "XXX", 3) == 3);
198 ATF_REQUIRE(close(fd) == 0);
199 ATF_REQUIRE(chmod(path, 0444) == 0);
200
201 fd = open(path, O_RDONLY);
202 ATF_REQUIRE(fd != -1);
203
204 map = mmap(NULL, 3, PROT_NONE, MAP_FILE | MAP_SHARED, fd, 0);
205 ATF_REQUIRE(map != MAP_FAILED);
206
207 pid = fork();
208
209 ATF_REQUIRE(pid >= 0);
210
211 if (pid == 0) {
212 ATF_REQUIRE(signal(SIGSEGV, map_sighandler) != SIG_ERR);
213 ATF_REQUIRE(strlcpy(buf, map, sizeof(buf)) != 0);
214 }
215
216 (void)wait(&sta);
217
218 ATF_REQUIRE(WIFEXITED(sta) != 0);
219 ATF_REQUIRE(WEXITSTATUS(sta) == SIGSEGV);
220 ATF_REQUIRE(munmap(map, 3) == 0);
221 }
222
223 ATF_TC_CLEANUP(mmap_prot_3, tc)
224 {
225 (void)unlink(path);
226 }
227
228 ATF_TC_WITH_CLEANUP(mmap_truncate);
229 ATF_TC_HEAD(mmap_truncate, tc)
230 {
231 atf_tc_set_md_var(tc, "descr", "Test mmap(2) and ftruncate(2)");
232 }
233
234 ATF_TC_BODY(mmap_truncate, tc)
235 {
236 char *map;
237 long i;
238 int fd;
239
240 fd = open(path, O_RDWR | O_CREAT, 0700);
241
242 if (fd < 0)
243 return;
244
245 /*
246 * See that ftruncate(2) works
247 * while the file is mapped.
248 */
249 ATF_REQUIRE(ftruncate(fd, page) == 0);
250
251 map = mmap(NULL, page, PROT_READ | PROT_WRITE, MAP_FILE|MAP_PRIVATE,
252 fd, 0);
253 ATF_REQUIRE(map != MAP_FAILED);
254
255 for (i = 0; i < page; i++)
256 map[i] = 'x';
257
258 ATF_REQUIRE(ftruncate(fd, 0) == 0);
259 ATF_REQUIRE(ftruncate(fd, page / 8) == 0);
260 ATF_REQUIRE(ftruncate(fd, page / 4) == 0);
261 ATF_REQUIRE(ftruncate(fd, page / 2) == 0);
262 ATF_REQUIRE(ftruncate(fd, page / 12) == 0);
263 ATF_REQUIRE(ftruncate(fd, page / 64) == 0);
264
265 ATF_REQUIRE(close(fd) == 0);
266 }
267
268 ATF_TC_CLEANUP(mmap_truncate, tc)
269 {
270 (void)unlink(path);
271 }
272
273 ATF_TC(mmap_va0);
274 ATF_TC_HEAD(mmap_va0, tc)
275 {
276 atf_tc_set_md_var(tc, "descr", "Test mmap(2) and vm.user_va0_disable");
277 }
278
279 ATF_TC_BODY(mmap_va0, tc)
280 {
281 int flags = MAP_ANON | MAP_FIXED | MAP_PRIVATE;
282 size_t len = sizeof(int);
283 void *map;
284 int val;
285
286 /*
287 * Make an anonymous fixed mapping at zero address. If the address
288 * is restricted as noted in security(7), the syscall should fail.
289 */
290 if (sysctlbyname("vm.user_va0_disable", &val, &len, NULL, 0) != 0)
291 atf_tc_fail("failed to read vm.user_va0_disable");
292
293 map = mmap(NULL, page, PROT_EXEC, flags, -1, 0);
294 map_check(map, val);
295
296 map = mmap(NULL, page, PROT_READ, flags, -1, 0);
297 map_check(map, val);
298
299 map = mmap(NULL, page, PROT_WRITE, flags, -1, 0);
300 map_check(map, val);
301
302 map = mmap(NULL, page, PROT_READ|PROT_WRITE, flags, -1, 0);
303 map_check(map, val);
304
305 map = mmap(NULL, page, PROT_EXEC|PROT_READ|PROT_WRITE, flags, -1, 0);
306 map_check(map, val);
307 }
308
309 ATF_TP_ADD_TCS(tp)
310 {
311 page = sysconf(_SC_PAGESIZE);
312 ATF_REQUIRE(page >= 0);
313
314 ATF_TP_ADD_TC(tp, mmap_err);
315 ATF_TP_ADD_TC(tp, mmap_prot_1);
316 ATF_TP_ADD_TC(tp, mmap_prot_2);
317 ATF_TP_ADD_TC(tp, mmap_prot_3);
318 ATF_TP_ADD_TC(tp, mmap_truncate);
319 ATF_TP_ADD_TC(tp, mmap_va0);
320
321 return atf_no_error();
322 }
323