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