1 /* 2 * Copyright (c) 2014 Fujitsu Ltd. 3 * Author: Xing Gu <gux.fnst (at) 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 "config.h" 28 #include <signal.h> 29 #include <setjmp.h> 30 #include <sys/types.h> 31 #include <sys/stat.h> 32 #include <fcntl.h> 33 #include <unistd.h> 34 #include <errno.h> 35 #include <string.h> 36 #include <sys/mman.h> 37 #include <stdlib.h> 38 39 #include "test.h" 40 #include "safe_macros.h" 41 42 static void sighandler(int sig); 43 44 static void setup(void); 45 static void cleanup(void); 46 47 static void testfunc_protnone(void); 48 49 static void testfunc_protexec(void); 50 51 static void (*testfunc[])(void) = { testfunc_protnone, testfunc_protexec }; 52 53 char *TCID = "mprotect04"; 54 int TST_TOTAL = ARRAY_SIZE(testfunc); 55 56 static volatile int sig_caught; 57 static sigjmp_buf env; 58 static unsigned int copy_sz; 59 60 int main(int ac, char **av) 61 { 62 int lc; 63 int i; 64 65 tst_parse_opts(ac, av, NULL, NULL); 66 67 setup(); 68 69 for (lc = 0; TEST_LOOPING(lc); lc++) { 70 tst_count = 0; 71 72 for (i = 0; i < TST_TOTAL; i++) 73 (*testfunc[i])(); 74 } 75 76 cleanup(); 77 tst_exit(); 78 } 79 80 static void sighandler(int sig) 81 { 82 sig_caught = sig; 83 siglongjmp(env, 1); 84 } 85 86 static void setup(void) 87 { 88 tst_tmpdir(); 89 tst_sig(NOFORK, sighandler, cleanup); 90 copy_sz = getpagesize() * 2; 91 92 TEST_PAUSE; 93 } 94 95 static void testfunc_protnone(void) 96 { 97 char *addr; 98 int page_sz; 99 100 sig_caught = 0; 101 102 page_sz = getpagesize(); 103 104 addr = SAFE_MMAP(cleanup, 0, page_sz, PROT_READ | PROT_WRITE, 105 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 106 107 /* Change the protection to PROT_NONE. */ 108 TEST(mprotect(addr, page_sz, PROT_NONE)); 109 110 if (TEST_RETURN == -1) { 111 tst_resm(TFAIL | TTERRNO, "mprotect failed"); 112 } else { 113 if (sigsetjmp(env, 1) == 0) 114 addr[0] = 1; 115 116 switch (sig_caught) { 117 case SIGSEGV: 118 tst_resm(TPASS, "test PROT_NONE for mprotect success"); 119 break; 120 case 0: 121 tst_resm(TFAIL, "test PROT_NONE for mprotect failed"); 122 break; 123 default: 124 tst_brkm(TBROK, cleanup, 125 "received an unexpected signal: %d", 126 sig_caught); 127 } 128 } 129 130 SAFE_MUNMAP(cleanup, addr, page_sz); 131 } 132 133 #ifdef __ia64__ 134 135 static char exec_func[] = { 136 0x11, 0x00, 0x00, 0x00, 0x01, 0x00, /* nop.m 0x0 */ 137 0x00, 0x00, 0x00, 0x02, 0x00, 0x80, /* nop.i 0x0 */ 138 0x08, 0x00, 0x84, 0x00, /* br.ret.sptk.many b0;; */ 139 }; 140 141 struct func_desc { 142 uint64_t func_addr; 143 uint64_t glob_pointer; 144 }; 145 146 static __attribute__((noinline)) void *get_func(void *mem) 147 { 148 static struct func_desc fdesc; 149 150 memcpy(mem, exec_func, sizeof(exec_func)); 151 152 fdesc.func_addr = (uint64_t)mem; 153 fdesc.glob_pointer = 0; 154 155 return &fdesc; 156 } 157 158 #else 159 160 static void exec_func(void) 161 { 162 return; 163 } 164 165 static int page_present(void *p) 166 { 167 int fd; 168 169 fd = SAFE_OPEN(cleanup, "page_present", O_WRONLY|O_CREAT, 0644); 170 TEST(write(fd, p, 1)); 171 SAFE_CLOSE(cleanup, fd); 172 173 if (TEST_RETURN >= 0) 174 return 1; 175 176 if (TEST_ERRNO != EFAULT) 177 tst_brkm(TBROK | TTERRNO, cleanup, "page_present write"); 178 179 return 0; 180 } 181 182 static void clear_cache(void *start, int len) 183 { 184 #if HAVE_BUILTIN_CLEAR_CACHE == 1 185 __builtin___clear_cache(start, start + len); 186 #else 187 tst_brkm(TCONF, cleanup, 188 "compiler doesn't have __builtin___clear_cache()"); 189 #endif 190 } 191 192 /* 193 * Copy page where &exec_func resides. Also try to copy subsequent page 194 * in case exec_func is close to page boundary. 195 */ 196 static void *get_func(void *mem) 197 { 198 uintptr_t page_sz = getpagesize(); 199 uintptr_t page_mask = ~(page_sz - 1); 200 uintptr_t func_page_offset = (uintptr_t)&exec_func & (page_sz - 1); 201 void *func_copy_start = mem + func_page_offset; 202 void *page_to_copy = (void *)((uintptr_t)&exec_func & page_mask); 203 void *mem_start = mem; 204 205 /* copy 1st page, if it's not present something is wrong */ 206 if (!page_present(page_to_copy)) { 207 tst_resm(TINFO, "exec_func: %p, page_to_copy: %p\n", 208 &exec_func, page_to_copy); 209 tst_brkm(TBROK, cleanup, "page_to_copy not present\n"); 210 } 211 memcpy(mem, page_to_copy, page_sz); 212 213 /* copy 2nd page if possible */ 214 mem += page_sz; 215 page_to_copy += page_sz; 216 if (page_present(page_to_copy)) 217 memcpy(mem, page_to_copy, page_sz); 218 else 219 memset(mem, 0, page_sz); 220 221 clear_cache(mem_start, copy_sz); 222 223 /* return pointer to area where copy of exec_func resides */ 224 return func_copy_start; 225 } 226 227 #endif 228 229 static void testfunc_protexec(void) 230 { 231 void (*func)(void); 232 void *p; 233 234 sig_caught = 0; 235 236 p = SAFE_MMAP(cleanup, 0, copy_sz, PROT_READ | PROT_WRITE, 237 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 238 239 func = get_func(p); 240 241 /* Change the protection to PROT_EXEC. */ 242 TEST(mprotect(p, copy_sz, PROT_EXEC)); 243 244 if (TEST_RETURN == -1) { 245 tst_resm(TFAIL | TTERRNO, "mprotect failed"); 246 } else { 247 if (sigsetjmp(env, 1) == 0) 248 (*func)(); 249 250 switch (sig_caught) { 251 case SIGSEGV: 252 tst_resm(TFAIL, "test PROT_EXEC for mprotect failed"); 253 break; 254 case 0: 255 tst_resm(TPASS, "test PROT_EXEC for mprotect success"); 256 break; 257 default: 258 tst_brkm(TBROK, cleanup, 259 "received an unexpected signal: %d", 260 sig_caught); 261 } 262 } 263 264 SAFE_MUNMAP(cleanup, p, copy_sz); 265 } 266 267 static void cleanup(void) 268 { 269 tst_rmdir(); 270 } 271