1#include <mach/mach.h> 2#include <stdio.h> 3#include <stdlib.h> 4#include <err.h> 5#include <pthread.h> 6#include <sys/mman.h> 7#include <dispatch/dispatch.h> 8#include <sys/sysctl.h> 9 10#include "excserver.h" 11 12/* 13 * Test program that sets up a Mach exception handler, 14 * then performs 1000 invalid memory accesses and makes 15 * sure all thread_get_state variants can be executed 16 * from inside the exception handler. 17 */ 18void *handler(void *); 19void *spin(void *); 20dispatch_semaphore_t start_sema; 21volatile int iteration; 22 23#define COUNT 10000 24 25int main(int argc, char *argv[]) { 26 int ret; 27 pthread_t handle_thread; 28 char *buffer = valloc(4096); 29 int i; 30 int ncpu; 31 size_t ncpucount = sizeof(ncpu); 32 33 start_sema = dispatch_semaphore_create(0); 34 35 ret = sysctlbyname("hw.ncpu", &ncpu, &ncpucount, NULL, 0); 36 if (ret) 37 err(1, "sysctlbyname"); 38 39 for (i=0; i < ncpu; i++) { 40 pthread_t spin_thread; 41 42 ret = pthread_create(&spin_thread, NULL, spin, NULL); 43 if (ret) 44 err(1, "pthread_create"); 45 } 46 47 sleep(1); 48 ret = pthread_create(&handle_thread, NULL, handler, NULL); 49 if (ret) 50 err(1, "pthread_create"); 51 52 dispatch_semaphore_wait(start_sema, DISPATCH_TIME_FOREVER); 53 54 for (iteration = 0; iteration < COUNT; iteration++) { 55 ret = mprotect(buffer, 4096, PROT_NONE); 56 if (ret != 0) 57 err(1, "mprotect"); 58 59 usleep(1000); 60 61 volatile float a = ((float)iteration)/2.4f; 62 *buffer = '!'; 63 } 64 65 return 0; 66} 67 68void *handler(void *arg __unused) { 69 kern_return_t kret; 70 mach_port_t exception_port; 71 72 kret = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, 73 &exception_port); 74 if (kret != KERN_SUCCESS) 75 errx(1, "mach_port_allocate: %s (%d)", mach_error_string(kret), kret); 76 77 kret = mach_port_insert_right(mach_task_self(), exception_port, exception_port, MACH_MSG_TYPE_MAKE_SEND); 78 if (kret != KERN_SUCCESS) 79 errx(1, "mach_port_insert_right: %s (%d)", mach_error_string(kret), kret); 80 81 kret = task_set_exception_ports(mach_task_self(), 82 EXC_MASK_BAD_ACCESS, 83 exception_port, 84 EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, 85 0); 86 if (kret != KERN_SUCCESS) 87 errx(1, "task_set_exception_ports: %s (%d)", mach_error_string(kret), kret); 88 89 dispatch_semaphore_signal(start_sema); 90 91 kret = mach_msg_server(mach_exc_server, MACH_MSG_SIZE_RELIABLE, exception_port, 0); 92 if (kret != KERN_SUCCESS) 93 errx(1, "mach_msg_server: %s (%d)", mach_error_string(kret), kret); 94 95 return NULL; 96} 97 98kern_return_t catch_mach_exception_raise 99( 100 mach_port_t exception_port, 101 mach_port_t thread, 102 mach_port_t task, 103 exception_type_t exception, 104 mach_exception_data_t code, 105 mach_msg_type_number_t codeCnt 106) 107{ 108 int ret; 109 kern_return_t kret; 110 thread_state_flavor_t flavors[128]; 111 thread_state_data_t state; 112 mach_msg_type_number_t count; 113 int i, flcount; 114 115// printf("Successfully caught EXC_BAD_ACCESS %s(%d) at 0x%016llx\n", mach_error_string((int)code[0]), (int)code[0], code[1]); 116 117 count = sizeof(flavors)/sizeof(natural_t); 118 kret = thread_get_state(thread, THREAD_STATE_FLAVOR_LIST_NEW, (thread_state_t)flavors, &count); 119 if (kret == KERN_INVALID_ARGUMENT) { 120 /* try older query */ 121 count = sizeof(flavors)/sizeof(natural_t); 122 kret = thread_get_state(thread, THREAD_STATE_FLAVOR_LIST, (thread_state_t)flavors, &count); 123 if (kret != KERN_SUCCESS) 124 errx(1, "thread_get_state(THREAD_STATE_FLAVOR_LIST): %s (%d)", mach_error_string(kret), kret); 125 } else if (kret != KERN_SUCCESS) 126 errx(1, "thread_get_state(THREAD_STATE_FLAVOR_LIST_NEW): %s (%d)", mach_error_string(kret), kret); 127 128 flcount = count; 129 for (i=0; i < flcount; i++) { 130 thread_state_flavor_t flavor; 131 132 flavor = flavors[(i + iteration) % flcount]; 133 count = THREAD_STATE_MAX; 134 kret = thread_get_state(thread, flavor, (thread_state_t)state, &count); 135 if (kret != KERN_SUCCESS) 136 errx(1, "thread_get_state(%d): %s (%d)", flavor, mach_error_string(kret), kret); 137 } 138 139 ret = mprotect((void *)code[1], 4096, PROT_WRITE); 140 if (ret != 0) 141 err(1, "mprotect"); 142 143 return KERN_SUCCESS; 144} 145 146kern_return_t catch_mach_exception_raise_state 147( 148 mach_port_t exception_port, 149 exception_type_t exception, 150 const mach_exception_data_t code, 151 mach_msg_type_number_t codeCnt, 152 int *flavor, 153 const thread_state_t old_state, 154 mach_msg_type_number_t old_stateCnt, 155 thread_state_t new_state, 156 mach_msg_type_number_t *new_stateCnt 157) 158{ 159 errx(1, "Unsupported catch_mach_exception_raise_state"); 160 return KERN_NOT_SUPPORTED; 161} 162 163kern_return_t catch_mach_exception_raise_state_identity 164( 165 mach_port_t exception_port, 166 mach_port_t thread, 167 mach_port_t task, 168 exception_type_t exception, 169 mach_exception_data_t code, 170 mach_msg_type_number_t codeCnt, 171 int *flavor, 172 thread_state_t old_state, 173 mach_msg_type_number_t old_stateCnt, 174 thread_state_t new_state, 175 mach_msg_type_number_t *new_stateCnt 176) 177{ 178 errx(1, "Unsupported catch_mach_exception_raise_state_identity"); 179 return KERN_NOT_SUPPORTED; 180} 181 182void *spin(void *arg __unused) { 183 volatile unsigned int a; 184 185 while (1) { 186 a++; 187 } 188 189 return NULL; 190} 191