]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libc/sys/t_mprotect.c
Update ^/vendor/NetBSD/tests/dist to a more recent snapshot
[FreeBSD/FreeBSD.git] / lib / libc / sys / t_mprotect.c
1 /* $NetBSD: t_mprotect.c,v 1.4 2016/05/28 14:34:49 christos 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_mprotect.c,v 1.4 2016/05/28 14:34:49 christos 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 <stdlib.h>
42 #include <string.h>
43 #include <unistd.h>
44
45 #include <atf-c.h>
46
47 #include "../common/exec_prot.h"
48
49 static long     page = 0;
50 static int      pax_global = -1;
51 static int      pax_enabled = -1;
52 static char     path[] = "mmap";
53
54 static void     sighandler(int);
55 static bool     paxinit(void);
56 static bool     paxset(int, int);
57
58 static void
59 sighandler(int signo)
60 {
61         _exit(signo);
62 }
63
64 static bool
65 paxinit(void)
66 {
67         size_t len = sizeof(int);
68         int rv;
69
70         rv = sysctlbyname("security.pax.mprotect.global",
71             &pax_global, &len, NULL, 0);
72
73         if (rv != 0)
74                 return false;
75
76         rv = sysctlbyname("security.pax.mprotect.enabled",
77             &pax_enabled, &len, NULL, 0);
78
79         return rv == 0;
80 }
81
82 static bool
83 paxset(int global, int enabled)
84 {
85         size_t len = sizeof(int);
86         int rv;
87
88         rv = sysctlbyname("security.pax.mprotect.global",
89             NULL, NULL, &global, len);
90
91         if (rv != 0)
92                 return false;
93
94         rv = sysctlbyname("security.pax.mprotect.enabled",
95             NULL, NULL, &enabled, len);
96
97         if (rv != 0)
98                 return false;
99
100         return true;
101 }
102
103
104 ATF_TC_WITH_CLEANUP(mprotect_access);
105 ATF_TC_HEAD(mprotect_access, tc)
106 {
107         atf_tc_set_md_var(tc, "descr", "Test for EACCES from mprotect(2)");
108 }
109
110 ATF_TC_BODY(mprotect_access, tc)
111 {
112         int prot[2] = { PROT_NONE, PROT_READ };
113         void *map;
114         size_t i;
115         int fd;
116
117         fd = open(path, O_RDONLY | O_CREAT);
118         ATF_REQUIRE(fd >= 0);
119
120         /*
121          * The call should fail with EACCES if we try to mark
122          * a PROT_NONE or PROT_READ file/section as PROT_WRITE.
123          */
124         for (i = 0; i < __arraycount(prot); i++) {
125
126                 map = mmap(NULL, page, prot[i], MAP_SHARED, fd, 0);
127
128                 if (map == MAP_FAILED)
129                         continue;
130
131                 errno = 0;
132
133                 ATF_REQUIRE(mprotect(map, page, PROT_WRITE) != 0);
134                 ATF_REQUIRE(errno == EACCES);
135                 ATF_REQUIRE(munmap(map, page) == 0);
136         }
137
138         ATF_REQUIRE(close(fd) == 0);
139 }
140
141 ATF_TC_CLEANUP(mprotect_access, tc)
142 {
143         (void)unlink(path);
144 }
145
146 ATF_TC(mprotect_err);
147 ATF_TC_HEAD(mprotect_err, tc)
148 {
149         atf_tc_set_md_var(tc, "descr", "Test error conditions of mprotect(2)");
150 }
151
152 ATF_TC_BODY(mprotect_err, tc)
153 {
154         errno = 0;
155
156         ATF_REQUIRE(mprotect((char *)-1, 1, PROT_READ) != 0);
157         ATF_REQUIRE(errno == EINVAL);
158 }
159
160 ATF_TC(mprotect_exec);
161 ATF_TC_HEAD(mprotect_exec, tc)
162 {
163         atf_tc_set_md_var(tc, "descr",
164             "Test mprotect(2) executable space protections");
165 }
166
167 /*
168  * Trivial function -- should fit into a page
169  */
170 ATF_TC_BODY(mprotect_exec, tc)
171 {
172         pid_t pid;
173         void *map;
174         int sta, xp_support;
175
176         xp_support = exec_prot_support();
177
178         switch (xp_support) {
179         case NOTIMPL:
180                 atf_tc_skip(
181                     "Execute protection callback check not implemented");
182                 break;
183         case NO_XP:
184                 atf_tc_skip(
185                     "Host does not support executable space protection");
186                 break;
187         case PARTIAL_XP: case PERPAGE_XP: default:
188                 break;
189         }
190
191         if (!paxinit())
192                 return;
193         if (pax_enabled == 1 && pax_global == 1)
194                 atf_tc_skip("PaX MPROTECT restrictions enabled");
195                 
196
197         /*
198          * Map a page read/write and copy a trivial assembly function inside.
199          * We will then change the mapping rights:
200          * - first by setting the execution right, and check that we can
201          *   call the code found in the allocated page.
202          * - second by removing the execution right. This should generate
203          *   a SIGSEGV on architectures that can enforce --x permissions.
204          */
205
206         map = mmap(NULL, page, PROT_WRITE|PROT_READ, MAP_ANON, -1, 0);
207         ATF_REQUIRE(map != MAP_FAILED);
208
209         memcpy(map, (void *)return_one,
210             (uintptr_t)return_one_end - (uintptr_t)return_one);
211  
212         /* give r-x rights then call code in page */
213         ATF_REQUIRE(mprotect(map, page, PROT_EXEC|PROT_READ) == 0);
214         ATF_REQUIRE(((int (*)(void))map)() == 1);
215
216         /* remove --x right */
217         ATF_REQUIRE(mprotect(map, page, PROT_READ) == 0);
218
219         pid = fork();
220         ATF_REQUIRE(pid >= 0);
221
222         if (pid == 0) {
223                 ATF_REQUIRE(signal(SIGSEGV, sighandler) != SIG_ERR);
224                 ATF_CHECK(((int (*)(void))map)() == 1);
225                 _exit(0);
226         }
227
228         (void)wait(&sta);
229
230         ATF_REQUIRE(munmap(map, page) == 0);
231
232         ATF_REQUIRE(WIFEXITED(sta) != 0);
233
234         switch (xp_support) {
235         case PARTIAL_XP:
236                 /* Partial protection might fail; skip the test when it does */
237                 if (WEXITSTATUS(sta) != SIGSEGV) {
238                         atf_tc_skip("Host only supports "
239                             "partial executable space protection");
240                 }
241                 break;
242         case PERPAGE_XP: default:
243                 /* Per-page --x protection should not fail */
244                 ATF_REQUIRE(WEXITSTATUS(sta) == SIGSEGV);
245                 break;
246         }
247 }
248
249 ATF_TC(mprotect_pax);
250 ATF_TC_HEAD(mprotect_pax, tc)
251 {
252         atf_tc_set_md_var(tc, "descr", "PaX restrictions and mprotect(2)");
253         atf_tc_set_md_var(tc, "require.user", "root");
254 }
255
256 ATF_TC_BODY(mprotect_pax, tc)
257 {
258         const int prot[4] = { PROT_NONE, PROT_READ, PROT_WRITE };
259         const char *str = NULL;
260         void *map;
261         size_t i;
262         int rv;
263
264         if (!paxinit() || !paxset(1, 1))
265                 return;
266
267         /*
268          * As noted in the original PaX documentation [1],
269          * the following restrictions should apply:
270          *
271          *   (1) creating executable anonymous mappings
272          *
273          *   (2) creating executable/writable file mappings
274          *
275          *   (3) making a non-executable mapping executable
276          *
277          *   (4) making an executable/read-only file mapping
278          *       writable except for performing relocations
279          *       on an ET_DYN ELF file (non-PIC shared library)
280          *
281          *  The following will test only the case (3).
282          *
283          * [1] http://pax.grsecurity.net/docs/mprotect.txt
284          *
285          *     (Sun Apr 3 11:06:53 EEST 2011.)
286          */
287         for (i = 0; i < __arraycount(prot); i++) {
288
289                 map = mmap(NULL, page, prot[i], MAP_ANON, -1, 0);
290
291                 if (map == MAP_FAILED)
292                         continue;
293
294                 rv = mprotect(map, 1, prot[i] | PROT_EXEC);
295
296                 (void)munmap(map, page);
297
298                 if (rv == 0) {
299                         str = "non-executable mapping made executable";
300                         goto out;
301                 }
302         }
303
304 out:
305         if (pax_global != -1 && pax_enabled != -1)
306                 (void)paxset(pax_global, pax_enabled);
307
308         if (str != NULL)
309                 atf_tc_fail("%s", str);
310 }
311
312 ATF_TC(mprotect_write);
313 ATF_TC_HEAD(mprotect_write, tc)
314 {
315         atf_tc_set_md_var(tc, "descr", "Test mprotect(2) write protections");
316 }
317
318 ATF_TC_BODY(mprotect_write, tc)
319 {
320         pid_t pid;
321         void *map;
322         int sta;
323
324         /*
325          * Map a page read/write, change the protection
326          * to read-only with mprotect(2), and try to write
327          * to the page. This should generate a SIGSEGV.
328          */
329         map = mmap(NULL, page, PROT_WRITE|PROT_READ, MAP_ANON, -1, 0);
330         ATF_REQUIRE(map != MAP_FAILED);
331
332         ATF_REQUIRE(strlcpy(map, "XXX", 3) == 3);
333         ATF_REQUIRE(mprotect(map, page, PROT_READ) == 0);
334
335         pid = fork();
336         ATF_REQUIRE(pid >= 0);
337
338         if (pid == 0) {
339                 ATF_REQUIRE(signal(SIGSEGV, sighandler) != SIG_ERR);
340                 ATF_REQUIRE(strlcpy(map, "XXX", 3) == 0);
341         }
342
343         (void)wait(&sta);
344
345         ATF_REQUIRE(WIFEXITED(sta) != 0);
346         ATF_REQUIRE(WEXITSTATUS(sta) == SIGSEGV);
347         ATF_REQUIRE(munmap(map, page) == 0);
348 }
349
350 ATF_TP_ADD_TCS(tp)
351 {
352         page = sysconf(_SC_PAGESIZE);
353         ATF_REQUIRE(page >= 0);
354
355         ATF_TP_ADD_TC(tp, mprotect_access);
356         ATF_TP_ADD_TC(tp, mprotect_err);
357         ATF_TP_ADD_TC(tp, mprotect_exec);
358         ATF_TP_ADD_TC(tp, mprotect_pax);
359         ATF_TP_ADD_TC(tp, mprotect_write);
360
361         return atf_no_error();
362 }