The Pedigree Project 0.1
mprotect.c
1/*
2 * Copyright (c) 2008-2014, Pedigree Developers
3 *
4 * Please see the CONTRIB file in the root of the source tree for a full
5 * list of contributors.
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20#include <setjmp.h>
21#include <signal.h>
22#include <stddef.h>
23#include <stdint.h>
24#include <stdio.h>
25#include <unistd.h>
26
27#include <sys/mman.h>
28
29static int i = 0;
30static char* p = 0;
31static size_t page_size = 0;
32static size_t mapping_size = 0;
33
34static jmp_buf buf;
35
36typedef void (*fn)();
37
38extern void fail();
39
40static void* adjust_pointer(void* p, ssize_t amt) {
41 return (void*)(((uintptr_t)p) + amt);
42}
43
44static void sigsegv(int s) {
45 if (i == -1) {
46 fail();
47 }
48
49 switch (i) {
50 case 0: {
51 printf("PROT_NONE works, checking read...\n");
52 mprotect(p, page_size, PROT_READ);
53 i = -1;
54 volatile char c = *p;
55 i = 0;
56 } break;
57
58 case 1: {
59 printf("PROT_READ works, checking write...\n");
60 mprotect(p, page_size, PROT_WRITE);
61 i = -1;
62 *((volatile char*)p) = 'Y';
63 i = 1;
64 mprotect(p, page_size, PROT_NONE);
65 } break;
66
67 case 2: {
68 printf("PROT_WRITE works, checking exec...\n");
69 mprotect(p, page_size, PROT_WRITE);
70 i = -1;
71 *((volatile unsigned char*)p) = 0xC3; // ret
72 fn f = (fn)p;
73 mprotect(p, page_size, PROT_EXEC);
74 f();
75 i = 2;
76 } break;
77
78 default:
79 printf("Attempting to return to original context...\n");
80 mprotect(p, page_size, PROT_READ | PROT_WRITE);
81 longjmp(buf, 1);
82 }
83
84 ++i;
85}
86
87static void sigsegv_jumper(int s) {
88 longjmp(buf, 1);
89}
90
91static void status(const char* s) {
92 printf("%s", s);
93 fflush(stdout);
94}
95
96void test_mprotect() {
97 int rc = 0;
98
99 const long configured_page_size = sysconf(_SC_PAGESIZE);
100 if (configured_page_size <= 0)
101 fail();
102 page_size = (size_t)configured_page_size;
103 mapping_size = 16 * page_size;
104
105 p = mmap(0, mapping_size, PROT_NONE, MAP_PRIVATE | MAP_ANON, 0, 0);
106 if (p == MAP_FAILED)
107 fail();
108
109 // Install our custom SIGSEGV handler.
110 static struct sigaction act;
111 sigprocmask(0, 0, &act.sa_mask);
112 act.sa_handler = sigsegv;
113#ifdef SA_NODEFER
114 act.sa_flags = SA_NODEFER;
115#else
116 act.sa_flags = 0; // Pedigree doesn't yet have SA_NODEFER (SIGSEGV nests anyway)
117#endif
118 sigaction(SIGSEGV, &act, 0);
119
120 // Start the test!
121 printf("Testing mprotect(2)...\n");
122 setjmp(buf);
123 *p = 'X';
124 printf("mprotect(2) initial test was successful!\n");
125
126 printf("Testing mprotect(2) on ranges of pages...\n");
127
128 i = -1;
129
130 act.sa_handler = sigsegv_jumper;
131 sigaction(SIGSEGV, &act, 0);
132
133 // Check for 100% coverage.
134 mprotect(p, mapping_size, PROT_WRITE);
135 status("Test A... ");
136 *p = 'X';
137 status("OK\n");
138 mprotect(p, mapping_size, PROT_NONE);
139
140 // Check for 100% enclosed.
141 mprotect(adjust_pointer(p, -((ssize_t)page_size)), mapping_size + (2 * page_size), PROT_WRITE);
142 status("Test B... ");
143 if (setjmp(buf) == 1)
144 fail();
145 *p = 'X';
146 p[mapping_size - 1] = 'X';
147 status("OK\n");
148 mprotect(p, mapping_size, PROT_NONE);
149
150 // Check for overlap at beginning.
151 mprotect(adjust_pointer(p, -((ssize_t)page_size)), 5 * page_size, PROT_WRITE);
152 status("Test C... ");
153 if (setjmp(buf) == 1)
154 fail();
155 *p = 'X';
156 if (setjmp(buf) == 0) {
157 p[(5 * page_size) + 1] = 'X';
158 fail();
159 }
160 status("OK\n");
161 mprotect(p, mapping_size, PROT_NONE);
162
163 // Check for overlap at end.
164 mprotect(adjust_pointer(p, 5 * page_size), 6 * page_size, PROT_WRITE);
165 status("Test D... ");
166 if (setjmp(buf) == 1)
167 fail();
168 p[5 * page_size] = 'X';
169 if (setjmp(buf) == 0) {
170 *p = 'X';
171 fail();
172 }
173 status("OK\n");
174 mprotect(p, mapping_size, PROT_NONE);
175
176 // Check middle.
177 mprotect(adjust_pointer(p, 2 * page_size), 6 * page_size, PROT_WRITE);
178 status("Test E... ");
179 if (setjmp(buf) == 1)
180 fail();
181 p[2 * page_size] = 'X';
182 if (setjmp(buf) == 0) {
183 *p = 'X';
184 fail();
185 }
186
187 if (setjmp(buf) == 0) {
188 p[(8 * page_size) + 1] = 'X';
189 fail();
190 }
191 status("OK\n");
192 mprotect(p, mapping_size, PROT_NONE);
193
194 printf("mprotect(2) page range test was successful!\n");
195
196 // Restore SIGSEGV handler.
197 signal(SIGSEGV, SIG_DFL);
198}