2 * Testing Framework for EXC_GUARD exceptions
4 * The framework tests for exception conditions for guarded fds.
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.
21 #include <mach/mach.h>
22 #include <sys/guarded.h>
23 #include <spawn_private.h>
24 #include <libproc_internal.h>
25 #include "guarded_test_common.h"
28 #define MAX_TEST_ID_LEN 16
30 #define EXC_GUARD_FLAVOR_SHIFT 32
31 #define EXC_GUARD_TYPE_SHIFT 61
32 #define EXC_GUARD_FD_MASK 0xFFFFFFFF
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 CHK_TEST_FD/IGN_TEST_FD depending on whether
40 * framework should look for TEST_FD in the exception message
41 * - Add a new case and routine in guarded_test.c to
47 uint64_t test_exception_code
[] = {
48 (((uint64_t)GUARD_TYPE_FD
) << EXC_GUARD_TYPE_SHIFT
) | (((uint64_t)kGUARD_EXC_CLOSE
) << EXC_GUARD_FLAVOR_SHIFT
),
49 (((uint64_t)GUARD_TYPE_FD
) << EXC_GUARD_TYPE_SHIFT
) | (((uint64_t)kGUARD_EXC_DUP
) << EXC_GUARD_FLAVOR_SHIFT
),
50 (((uint64_t)GUARD_TYPE_FD
) << EXC_GUARD_TYPE_SHIFT
) | (((uint64_t)kGUARD_EXC_NOCLOEXEC
) << EXC_GUARD_FLAVOR_SHIFT
),
54 (((uint64_t)GUARD_TYPE_FD
) << EXC_GUARD_TYPE_SHIFT
) | (((uint64_t)kGUARD_EXC_FILEPORT
) << EXC_GUARD_FLAVOR_SHIFT
),
55 (((uint64_t)GUARD_TYPE_FD
) << EXC_GUARD_TYPE_SHIFT
) | (((uint64_t)kGUARD_EXC_SOCKET_IPC
) << EXC_GUARD_FLAVOR_SHIFT
),
57 (((uint64_t)GUARD_TYPE_FD
) << EXC_GUARD_TYPE_SHIFT
) | (((uint64_t)kGUARD_EXC_CLOSE
) << EXC_GUARD_FLAVOR_SHIFT
),
58 (((uint64_t)GUARD_TYPE_FD
) << EXC_GUARD_TYPE_SHIFT
) | (((uint64_t)kGUARD_EXC_CLOSE
) << EXC_GUARD_FLAVOR_SHIFT
)
64 uint64_t test_fd
[] = {
79 uint64_t exception_code
;
80 extern char **environ
;
82 boolean_t
mach_exc_server(
83 mach_msg_header_t
*InHeadP
,
84 mach_msg_header_t
*OutHeadP
);
86 kern_return_t catch_mach_exception_raise
88 mach_port_t exception_port
,
91 exception_type_t exception
,
92 mach_exception_data_t code
,
93 mach_msg_type_number_t codeCnt
,
95 thread_state_t old_state
,
96 mach_msg_type_number_t old_stateCnt
,
97 thread_state_t new_state
,
98 mach_msg_type_number_t
*new_stateCnt
101 if (exception
== EXC_GUARD
) {
102 /* Set global variable to indicate exception received */
103 exception_code
= *((uint64_t *)code
);
105 /* Terminate test on all other unexpected exceptions */
106 fprintf(stderr
, "received unexpected exception type %#x\n", exception
);
110 return (KERN_SUCCESS
);
113 kern_return_t catch_mach_exception_raise_state
115 mach_port_t exception_port
,
116 exception_type_t exception
,
117 const mach_exception_data_t code
,
118 mach_msg_type_number_t codeCnt
,
120 const thread_state_t old_state
,
121 mach_msg_type_number_t old_stateCnt
,
122 thread_state_t new_state
,
123 mach_msg_type_number_t
*new_stateCnt
126 fprintf(stderr
, "Unexpected exception handler called\n");
128 return (KERN_FAILURE
);
132 kern_return_t catch_mach_exception_raise_state_identity
134 mach_port_t exception_port
,
137 exception_type_t exception
,
138 mach_exception_data_t code
,
139 mach_msg_type_number_t codeCnt
142 fprintf(stderr
, "Unexpected exception handler called\n");
144 return (KERN_FAILURE
);
148 void *server_thread(void *arg
)
153 /* Handle exceptions on exc_port */
154 if ((kr
= mach_msg_server_once(mach_exc_server
, 4096, exc_port
, 0)) != KERN_SUCCESS
) {
155 fprintf(stderr
, "mach_msg_server_once: error %#x\n", kr
);
162 int main(int argc
, char *argv
[])
164 posix_spawnattr_t attrs
;
166 mach_port_t task
= mach_task_self();
168 mach_msg_type_number_t maskCount
= 1;
169 exception_mask_t mask
;
170 exception_handler_t handler
;
171 exception_behavior_t behavior
;
172 thread_state_flavor_t flavor
;
173 pthread_t exception_thread
;
177 char *test_prog_name
= "./guarded_test";
178 char *child_args
[MAX_ARGV
];
179 char test_id
[MAX_TEST_ID_LEN
];
184 /* Allocate and initialize new exception port */
185 if ((kr
= mach_port_allocate(task
, MACH_PORT_RIGHT_RECEIVE
, &exc_port
)) != KERN_SUCCESS
) {
186 fprintf(stderr
, "mach_port_allocate: %#x\n", kr
);
190 if ((kr
= mach_port_insert_right(task
, exc_port
,
191 exc_port
, MACH_MSG_TYPE_MAKE_SEND
)) != KERN_SUCCESS
) {
192 fprintf(stderr
, "mach_port_allocate: %#x\n", kr
);
196 /* Get Current exception ports */
197 if ((kr
= task_get_exception_ports(task
, EXC_MASK_GUARD
, &mask
,
198 &maskCount
, &handler
, &behavior
, &flavor
)) != KERN_SUCCESS
) {
199 fprintf(stderr
,"task_get_exception_ports: %#x\n", kr
);
203 /* Create exception serving thread */
204 if ((err
= pthread_create(&exception_thread
, NULL
, server_thread
, 0)) != 0) {
205 fprintf(stderr
, "pthread_create server_thread: %s\n", strerror(err
));
209 pthread_detach(exception_thread
);
211 /* Initialize posix_spawn attributes */
212 posix_spawnattr_init(&attrs
);
214 if ((err
= posix_spawnattr_setflags(&attrs
, POSIX_SPAWN_SETEXEC
)) != 0) {
215 fprintf(stderr
, "posix_spawnattr_setflags: %s\n", strerror(err
));
220 for(i
=0; i
<NUMTESTS
; i
++) {
223 /* Set Exception Ports for Current Task */
224 if ((kr
= task_set_exception_ports(task
, EXC_MASK_GUARD
, exc_port
,
225 EXCEPTION_DEFAULT
| MACH_EXCEPTION_CODES
, flavor
)) != KERN_SUCCESS
) {
226 fprintf(stderr
, "task_set_exception_ports: %#x\n", kr
);
230 child_args
[0] = test_prog_name
;
231 sprintf(&test_id
[0], "%d", i
);
232 child_args
[1] = &test_id
[0];
233 child_args
[2] = NULL
;
235 /* Fork and exec child */
237 if ((err
= posix_spawn(NULL
, child_args
[0], NULL
, &attrs
, &child_args
[0], environ
)) != 0) {
238 fprintf(stderr
, "posix_spawn: %s\n", strerror(err
));
243 /* Restore exception ports for parent */
244 if ((kr
= task_set_exception_ports(task
, EXC_MASK_GUARD
, handler
,
245 EXCEPTION_DEFAULT
| MACH_EXCEPTION_CODES
, flavor
)) != KERN_SUCCESS
) {
246 fprintf(stderr
, "task_set_exception_ports: %#x\n", kr
);
250 /* Wait for child and check for exception */
251 if (-1 == wait4(-1, &child_status
, 0, NULL
)) {
255 exc_fd
= (unsigned int) (exception_code
& (uint64_t)EXC_GUARD_FD_MASK
);
256 exc_id
= exception_code
& ~((uint64_t)EXC_GUARD_FD_MASK
);
257 printf("EXC_GUARD Received: ");
258 (exception_code
!= 0)?printf("Yes (Code 0x%llx)\n", exception_code
):printf("No\n");
259 printf("Test Result: ");
260 if((WIFEXITED(child_status
) && WEXITSTATUS(child_status
)) ||
261 (exc_id
!= test_exception_code
[i
]) ||
262 (test_fd
[i
] && exc_fd
!= (unsigned int)TEST_FD
)) {
269 printf("-------------------\n");