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 <sys/mman.h>
26 #include <unistd.h>
27 
28 static int i = 0;
29 static char *p = 0;
30 
31 static jmp_buf buf;
32 
33 typedef void (*fn)();
34 
35 extern void fail();
36 
37 static void *adjust_pointer(void *p, ssize_t amt)
38 {
39  return (void *) (((uintptr_t) p) + amt);
40 }
41 
42 static void sigsegv(int s)
43 {
44  if (i == -1)
45  {
46  fail();
47  }
48 
49  switch (i)
50  {
51  case 0:
52  {
53  printf("PROT_NONE works, checking read...\n");
54  mprotect(p, 0x1000, PROT_READ);
55  i = -1;
56  volatile char c = *p;
57  i = 0;
58  }
59  break;
60 
61  case 1:
62  {
63  printf("PROT_READ works, checking write...\n");
64  mprotect(p, 0x1000, PROT_WRITE);
65  i = -1;
66  *((volatile char *) p) = 'Y';
67  i = 1;
68  mprotect(p, 0x1000, PROT_NONE);
69  }
70  break;
71 
72  case 2:
73  {
74  printf("PROT_WRITE works, checking exec...\n");
75  mprotect(p, 0x1000, PROT_WRITE);
76  i = -1;
77  *((volatile unsigned char *) p) = 0xC3; // ret
78  fn f = (fn) p;
79  mprotect(p, 0x1000, PROT_EXEC);
80  f();
81  i = 2;
82  }
83  break;
84 
85  default:
86  printf("Attempting to return to original context...\n");
87  mprotect(p, 0x1000, PROT_READ | PROT_WRITE);
88  longjmp(buf, 1);
89  }
90 
91  ++i;
92 }
93 
94 static void sigsegv_jumper(int s)
95 {
96  longjmp(buf, 1);
97 }
98 
99 static void status(const char *s)
100 {
101  printf(s);
102  fflush(stdout);
103 }
104 
105 void test_mprotect()
106 {
107  int rc = 0;
108 
109  p = mmap(0, 0x10000, PROT_NONE, MAP_PRIVATE | MAP_ANON, 0, 0);
110 
111  // Install our custom SIGSEGV handler.
112  static struct sigaction act;
113  sigprocmask(0, 0, &act.sa_mask);
114  act.sa_handler = sigsegv;
115 #ifdef SA_NODEFER
116  act.sa_flags = SA_NODEFER;
117 #else
118  act.sa_flags =
119  0; // Pedigree doesn't yet have SA_NODEFER (SIGSEGV nests anyway)
120 #endif
121  sigaction(SIGSEGV, &act, 0);
122 
123  // Start the test!
124  printf("Testing mprotect(2)...\n");
125  setjmp(buf);
126  *p = 'X';
127  printf("mprotect(2) initial test was successful!\n");
128 
129  printf("Testing mprotect(2) on ranges of pages...\n");
130 
131  i = -1;
132 
133  act.sa_handler = sigsegv_jumper;
134  sigaction(SIGSEGV, &act, 0);
135 
136  // Check for 100% coverage.
137  mprotect(p, 0x10000, PROT_WRITE);
138  status("Test A... ");
139  *p = 'X';
140  status("OK\n");
141  mprotect(p, 0x10000, PROT_NONE);
142 
143  // Check for 100% enclosed.
144  mprotect(adjust_pointer(p, -0x1000), 0x12000, PROT_WRITE);
145  status("Test B... ");
146  if (setjmp(buf) == 1)
147  fail();
148  *p = 'X';
149  p[0x10000 - 1] = 'X';
150  status("OK\n");
151  mprotect(p, 0x10000, PROT_NONE);
152 
153  // Check for overlap at beginning.
154  mprotect(adjust_pointer(p, -0x1000), 0x5000, PROT_WRITE);
155  status("Test C... ");
156  if (setjmp(buf) == 1)
157  fail();
158  *p = 'X';
159  if (setjmp(buf) == 0)
160  {
161  p[0x5001] = 'X';
162  fail();
163  }
164  status("OK\n");
165  mprotect(p, 0x10000, PROT_NONE);
166 
167  // Check for overlap at end.
168  mprotect(adjust_pointer(p, 0x5000), 0x6000, PROT_WRITE);
169  status("Test D... ");
170  if (setjmp(buf) == 1)
171  fail();
172  p[0x5000] = 'X';
173  if (setjmp(buf) == 0)
174  {
175  *p = 'X';
176  fail();
177  }
178  status("OK\n");
179  mprotect(p, 0x10000, PROT_NONE);
180 
181  // Check middle.
182  mprotect(adjust_pointer(p, 0x2000), 0x6000, PROT_WRITE);
183  status("Test E... ");
184  if (setjmp(buf) == 1)
185  fail();
186  p[0x2000] = 'X';
187  if (setjmp(buf) == 0)
188  {
189  *p = 'X';
190  fail();
191  }
192 
193  if (setjmp(buf) == 0)
194  {
195  p[0x8001] = 'X';
196  fail();
197  }
198  status("OK\n");
199  mprotect(p, 0x10000, PROT_NONE);
200 
201  printf("mprotect(2) page range test was successful!\n");
202 
203  // Restore SIGSEGV handler.
204  signal(SIGSEGV, SIG_DFL);
205 }