1/* 2 * Testing Framework for EXC_GUARD exceptions 3 * 4 * The framework tests for exception conditions for guarded mach ports. 5 * It creates a new exception port and an associated handling thread. 6 * For each test case, the framework sets its own exception port to the 7 * newly allocated port, execs a new child (which inherits the new 8 * exception port) and restores the parent's exception port to the 9 * original handler. The child process is invoked with a different 10 * test case identifier and invokes the corresponding test case. 11 * 12 */ 13 14#include <stdio.h> 15#include <spawn.h> 16#include <stdlib.h> 17#include <string.h> 18#include <unistd.h> 19#include <signal.h> 20#include <pthread.h> 21#include <mach/mach.h> 22#include <mach/port.h> 23#include <mach/mach_port.h> 24#include <mach/mach_init.h> 25#include <spawn_private.h> 26#include <libproc_internal.h> 27#include <mach_exc.h> 28 29#define MAX_TEST_ID_LEN 16 30#define MAX_ARGV 8 31#define EXC_CODE_SHIFT 32 32#define EXC_GUARD_TYPE_SHIFT 29 33 34/* 35 * To add a new test case to this framework: 36 * - Increment the NUMTESTS value 37 * - Add (Guard Type | flavor) to "test_exception_code" if the 38 * test case generates an exception; 0 otherwise 39 * - Add a new case and routine in guarded_test.c to 40 * test the scenario 41 */ 42 43#define NUMTESTS 10 44 45uint64_t test_exception_code[] = { 46 0, 47 (GUARD_TYPE_MACH_PORT << EXC_GUARD_TYPE_SHIFT) | kGUARD_EXC_DESTROY, 48 (GUARD_TYPE_MACH_PORT << EXC_GUARD_TYPE_SHIFT) | kGUARD_EXC_DESTROY, 49 (GUARD_TYPE_MACH_PORT << EXC_GUARD_TYPE_SHIFT) | kGUARD_EXC_MOD_REFS, 50 0, 51 (GUARD_TYPE_MACH_PORT << EXC_GUARD_TYPE_SHIFT) | kGUARD_EXC_INCORRECT_GUARD, 52 (GUARD_TYPE_MACH_PORT << EXC_GUARD_TYPE_SHIFT) | kGUARD_EXC_UNGUARDED, 53 0, 54 0, 55 (GUARD_TYPE_MACH_PORT << EXC_GUARD_TYPE_SHIFT) | kGUARD_EXC_SET_CONTEXT 56}; 57 58mach_port_t exc_port; 59uint64_t exception_code; 60extern char **environ; 61 62boolean_t mach_exc_server( 63 mach_msg_header_t *InHeadP, 64 mach_msg_header_t *OutHeadP); 65 66kern_return_t catch_mach_exception_raise 67( 68 mach_port_t exception_port, 69 mach_port_t thread, 70 mach_port_t task, 71 exception_type_t exception, 72 mach_exception_data_t code, 73 mach_msg_type_number_t codeCnt, 74 int *flavor, 75 thread_state_t old_state, 76 mach_msg_type_number_t old_stateCnt, 77 thread_state_t new_state, 78 mach_msg_type_number_t *new_stateCnt 79 ) 80{ 81 if (exception == EXC_GUARD) { 82 /* Set global variable to indicate exception received */ 83 exception_code = *((uint64_t *)code); 84 } else { 85 /* Terminate test on all other unexpected exceptions */ 86 fprintf(stderr, "received unexpected exception type %#x\n", exception); 87 exit(1); 88 } 89 90 return (KERN_SUCCESS); 91} 92 93kern_return_t catch_mach_exception_raise_state 94( 95 mach_port_t exception_port, 96 exception_type_t exception, 97 const mach_exception_data_t code, 98 mach_msg_type_number_t codeCnt, 99 int *flavor, 100 const thread_state_t old_state, 101 mach_msg_type_number_t old_stateCnt, 102 thread_state_t new_state, 103 mach_msg_type_number_t *new_stateCnt 104 ) 105{ 106 fprintf(stderr, "Unexpected exception handler called\n"); 107 exit(1); 108 return (KERN_FAILURE); 109} 110 111 112kern_return_t catch_mach_exception_raise_state_identity 113( 114 mach_port_t exception_port, 115 mach_port_t thread, 116 mach_port_t task, 117 exception_type_t exception, 118 mach_exception_data_t code, 119 mach_msg_type_number_t codeCnt 120 ) 121{ 122 fprintf(stderr, "Unexpected exception handler called\n"); 123 exit(1); 124 return (KERN_FAILURE); 125} 126 127 128void *server_thread(void *arg) 129{ 130 kern_return_t kr; 131 132 while(1) { 133 /* Handle exceptions on exc_port */ 134 if ((kr = mach_msg_server_once(mach_exc_server, 4096, exc_port, 0)) != KERN_SUCCESS) { 135 fprintf(stderr, "mach_msg_server_once: error %#x\n", kr); 136 exit(1); 137 } 138 } 139 return (NULL); 140} 141 142int main(int argc, char *argv[]) 143{ 144 posix_spawnattr_t attrs; 145 kern_return_t kr; 146 mach_port_t task = mach_task_self(); 147 148 mach_msg_type_number_t maskCount = 1; 149 exception_mask_t mask; 150 exception_handler_t handler; 151 exception_behavior_t behavior; 152 thread_state_flavor_t flavor; 153 pthread_t exception_thread; 154 uint64_t exc_id; 155 unsigned int exc_fd; 156 157 char *test_prog_name = "./guarded_mp_test"; 158 char *child_args[MAX_ARGV]; 159 char test_id[MAX_TEST_ID_LEN]; 160 int i, err; 161 int child_status; 162 int test_status = 0; 163 164 /* Allocate and initialize new exception port */ 165 if ((kr = mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE, &exc_port)) != KERN_SUCCESS) { 166 fprintf(stderr, "mach_port_allocate: %#x\n", kr); 167 exit(1); 168 } 169 170 if ((kr = mach_port_insert_right(task, exc_port, 171 exc_port, MACH_MSG_TYPE_MAKE_SEND)) != KERN_SUCCESS) { 172 fprintf(stderr, "mach_port_allocate: %#x\n", kr); 173 exit(1); 174 } 175 176 /* Get Current exception ports */ 177 if ((kr = task_get_exception_ports(task, EXC_MASK_GUARD, &mask, 178 &maskCount, &handler, &behavior, &flavor)) != KERN_SUCCESS) { 179 fprintf(stderr,"task_get_exception_ports: %#x\n", kr); 180 exit(1); 181 } 182 183 /* Create exception serving thread */ 184 if ((err = pthread_create(&exception_thread, NULL, server_thread, 0)) != 0) { 185 fprintf(stderr, "pthread_create server_thread: %s\n", strerror(err)); 186 exit(1); 187 } 188 189 pthread_detach(exception_thread); 190 191 /* Initialize posix_spawn attributes */ 192 posix_spawnattr_init(&attrs); 193 194 if ((err = posix_spawnattr_setflags(&attrs, POSIX_SPAWN_SETEXEC)) != 0) { 195 fprintf(stderr, "posix_spawnattr_setflags: %s\n", strerror(err)); 196 exit(1); 197 } 198 199 /* Run Tests */ 200 for(i=0; i<NUMTESTS; i++) { 201 202 exception_code = 0; 203 /* Set Exception Ports for Current Task */ 204 if ((kr = task_set_exception_ports(task, EXC_MASK_GUARD, exc_port, 205 EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, flavor)) != KERN_SUCCESS) { 206 fprintf(stderr, "task_set_exception_ports: %#x\n", kr); 207 exit(1); 208 } 209 210 child_args[0] = test_prog_name; 211 sprintf(&test_id[0], "%d", i); 212 child_args[1] = &test_id[0]; 213 child_args[2] = NULL; 214 215 /* Fork and exec child */ 216 if (fork() == 0) { 217 if ((err = posix_spawn(NULL, child_args[0], NULL, &attrs, &child_args[0], environ)) != 0) { 218 fprintf(stderr, "posix_spawn: %s\n", strerror(err)); 219 exit(1); 220 } 221 } 222 223 /* Restore exception ports for parent */ 224 if ((kr = task_set_exception_ports(task, EXC_MASK_GUARD, handler, 225 EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, flavor)) != KERN_SUCCESS) { 226 fprintf(stderr, "task_set_exception_ports: %#x\n", kr); 227 exit(1); 228 } 229 230 /* Wait for child and check for exception */ 231 if (-1 == wait4(-1, &child_status, 0, NULL)) { 232 exit(1); 233 } 234 235 exc_id = (exception_code >> EXC_CODE_SHIFT); 236 printf("EXC_GUARD Received: "); 237 (exc_id != 0)?printf("Yes (Code 0x%llx)\n", exception_code):printf("No\n"); 238 printf("Expected Exception Code: 0x%llx\n", test_exception_code[i]); 239 printf("Test Result: "); 240 if((WIFEXITED(child_status) && WEXITSTATUS(child_status)) || 241 (exc_id != test_exception_code[i])) { 242 test_status = 1; 243 printf("FAILED\n"); 244 } 245 else { 246 printf("PASSED\n"); 247 } 248 printf("-------------------\n"); 249 250 } 251 252 exit(test_status); 253} 254 255 256