1 /*
2 * Copyright (c) 2014 Fujitsu Ltd.
3 * Author: Xing Gu <gux.fnst@cn.fujitsu.com>
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it would be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write the Free Software Foundation, Inc.,
15 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
16 */
17 /*
18 * Description:
19 * Verify that,
20 * 1) mprotect() succeeds to set a region of memory with no access,
21 * when 'prot' is set to PROT_NONE. An attempt to access the contents
22 * of the region gives rise to the signal SIGSEGV.
23 * 2) mprotect() succeeds to set a region of memory to be executed, when
24 * 'prot' is set to PROT_EXEC.
25 */
26
27 #include <signal.h>
28 #include <setjmp.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <fcntl.h>
32 #include <unistd.h>
33 #include <errno.h>
34 #include <string.h>
35 #include <sys/mman.h>
36 #include <stdlib.h>
37
38 #include "test.h"
39 #include "safe_macros.h"
40
41 static void sighandler(int sig);
42
43 static void setup(void);
44 static void cleanup(void);
45
46 static void testfunc_protnone(void);
47
48 static void testfunc_protexec(void);
49
50 static void (*testfunc[])(void) = { testfunc_protnone, testfunc_protexec };
51
52 char *TCID = "mprotect04";
53 int TST_TOTAL = ARRAY_SIZE(testfunc);
54
55 static volatile int sig_caught;
56 static sigjmp_buf env;
57 static unsigned int copy_sz;
58
main(int ac,char ** av)59 int main(int ac, char **av)
60 {
61 int lc;
62 int i;
63
64 tst_parse_opts(ac, av, NULL, NULL);
65
66 setup();
67
68 for (lc = 0; TEST_LOOPING(lc); lc++) {
69 tst_count = 0;
70
71 for (i = 0; i < TST_TOTAL; i++)
72 (*testfunc[i])();
73 }
74
75 cleanup();
76 tst_exit();
77 }
78
sighandler(int sig)79 static void sighandler(int sig)
80 {
81 sig_caught = sig;
82 siglongjmp(env, 1);
83 }
84
setup(void)85 static void setup(void)
86 {
87 tst_tmpdir();
88 tst_sig(NOFORK, sighandler, cleanup);
89 copy_sz = getpagesize() * 2;
90
91 TEST_PAUSE;
92 }
93
testfunc_protnone(void)94 static void testfunc_protnone(void)
95 {
96 char *addr;
97 int page_sz;
98
99 sig_caught = 0;
100
101 page_sz = getpagesize();
102
103 addr = SAFE_MMAP(cleanup, 0, page_sz, PROT_READ | PROT_WRITE,
104 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
105
106 /* Change the protection to PROT_NONE. */
107 TEST(mprotect(addr, page_sz, PROT_NONE));
108
109 if (TEST_RETURN == -1) {
110 tst_resm(TFAIL | TTERRNO, "mprotect failed");
111 } else {
112 if (sigsetjmp(env, 1) == 0)
113 addr[0] = 1;
114
115 switch (sig_caught) {
116 case SIGSEGV:
117 tst_resm(TPASS, "test PROT_NONE for mprotect success");
118 break;
119 case 0:
120 tst_resm(TFAIL, "test PROT_NONE for mprotect failed");
121 break;
122 default:
123 tst_brkm(TBROK, cleanup,
124 "received an unexpected signal: %d",
125 sig_caught);
126 }
127 }
128
129 SAFE_MUNMAP(cleanup, addr, page_sz);
130 }
131
132 #ifdef __ia64__
133
134 static char exec_func[] = {
135 0x11, 0x00, 0x00, 0x00, 0x01, 0x00, /* nop.m 0x0 */
136 0x00, 0x00, 0x00, 0x02, 0x00, 0x80, /* nop.i 0x0 */
137 0x08, 0x00, 0x84, 0x00, /* br.ret.sptk.many b0;; */
138 };
139
140 struct func_desc {
141 uint64_t func_addr;
142 uint64_t glob_pointer;
143 };
144
get_func(void * mem)145 static __attribute__((noinline)) void *get_func(void *mem)
146 {
147 static struct func_desc fdesc;
148
149 memcpy(mem, exec_func, sizeof(exec_func));
150
151 fdesc.func_addr = (uint64_t)mem;
152 fdesc.glob_pointer = 0;
153
154 return &fdesc;
155 }
156
157 #else
158
exec_func(void)159 static void exec_func(void)
160 {
161 return;
162 }
163
page_present(void * p)164 static int page_present(void *p)
165 {
166 int fd;
167
168 fd = SAFE_OPEN(cleanup, "page_present", O_WRONLY|O_CREAT, 0644);
169 TEST(write(fd, p, 1));
170 SAFE_CLOSE(cleanup, fd);
171
172 if (TEST_RETURN >= 0)
173 return 1;
174
175 if (TEST_ERRNO != EFAULT)
176 tst_brkm(TBROK | TTERRNO, cleanup, "page_present write");
177
178 return 0;
179 }
180
181 /*
182 * Copy page where &exec_func resides. Also try to copy subsequent page
183 * in case exec_func is close to page boundary.
184 */
get_func(void * mem)185 static void *get_func(void *mem)
186 {
187 uintptr_t page_sz = getpagesize();
188 uintptr_t page_mask = ~(page_sz - 1);
189 uintptr_t func_page_offset = (uintptr_t)&exec_func & (page_sz - 1);
190 void *func_copy_start = mem + func_page_offset;
191 void *page_to_copy = (void *)((uintptr_t)&exec_func & page_mask);
192 #ifdef __powerpc__
193 void *mem_start = mem;
194 uintptr_t i;
195 #endif
196
197 /* copy 1st page, if it's not present something is wrong */
198 if (!page_present(page_to_copy)) {
199 tst_resm(TINFO, "exec_func: %p, page_to_copy: %p\n",
200 &exec_func, page_to_copy);
201 tst_brkm(TBROK, cleanup, "page_to_copy not present\n");
202 }
203 memcpy(mem, page_to_copy, page_sz);
204
205 /* copy 2nd page if possible */
206 mem += page_sz;
207 page_to_copy += page_sz;
208 if (page_present(page_to_copy))
209 memcpy(mem, page_to_copy, page_sz);
210 else
211 memset(mem, 0, page_sz);
212
213 #ifdef __powerpc__
214 for (i = 0; i < copy_sz; i += 4)
215 __asm__ __volatile__("dcbst 0,%0; sync; icbi 0,%0; sync; isync"
216 :: "r"(mem_start + i));
217 #endif
218
219 /* return pointer to area where copy of exec_func resides */
220 return func_copy_start;
221 }
222
223 #endif
224
testfunc_protexec(void)225 static void testfunc_protexec(void)
226 {
227 void (*func)(void);
228 void *p;
229
230 sig_caught = 0;
231
232 p = SAFE_MMAP(cleanup, 0, copy_sz, PROT_READ | PROT_WRITE,
233 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
234
235 func = get_func(p);
236
237 /* Change the protection to PROT_EXEC. */
238 TEST(mprotect(p, copy_sz, PROT_EXEC));
239
240 if (TEST_RETURN == -1) {
241 tst_resm(TFAIL | TTERRNO, "mprotect failed");
242 } else {
243 if (sigsetjmp(env, 1) == 0)
244 (*func)();
245
246 switch (sig_caught) {
247 case SIGSEGV:
248 tst_resm(TFAIL, "test PROT_EXEC for mprotect failed");
249 break;
250 case 0:
251 tst_resm(TPASS, "test PROT_EXEC for mprotect success");
252 break;
253 default:
254 tst_brkm(TBROK, cleanup,
255 "received an unexpected signal: %d",
256 sig_caught);
257 }
258 }
259
260 SAFE_MUNMAP(cleanup, p, copy_sz);
261 }
262
cleanup(void)263 static void cleanup(void)
264 {
265 tst_rmdir();
266 }
267