]>
Commit | Line | Data |
---|---|---|
70ad1dc8 A |
1 | /* |
2 | * Copyright (c) 2018 Apple Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
6 | * This file contains Original Code and/or Modifications of Original Code | |
7 | * as defined in and that are subject to the Apple Public Source License | |
8 | * Version 2.0 (the 'License'). You may not use this file except in | |
9 | * compliance with the License. Please obtain a copy of the License at | |
10 | * http://www.opensource.apple.com/apsl/ and read it before using this | |
11 | * file. | |
12 | * | |
13 | * The Original Code and all software distributed under the License are | |
14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
18 | * Please see the License for the specific language governing rights and | |
19 | * limitations under the License. | |
20 | * | |
21 | * @APPLE_LICENSE_HEADER_END@ | |
22 | */ | |
23 | #include "internal.h" | |
24 | ||
25 | #pragma mark Assertions | |
26 | static_assert(_OS_CRASH_LAST < OS_CRASH_PORT_ARRAY_COUNT, | |
27 | "array not large enough"); | |
28 | ||
29 | #pragma mark Private Utilities | |
30 | static thread_state_flavor_t | |
31 | _exception_thread_state_flavor(void) | |
32 | { | |
33 | thread_state_flavor_t flavor = 0; | |
34 | #if defined(__i386__) || defined(__x86_64__) | |
35 | flavor = x86_THREAD_STATE; | |
36 | #elif defined(__arm__) || defined(__arm64__) | |
37 | flavor = ARM_THREAD_STATE; | |
38 | #else // defined(__i386__) || defined(__x86_64__) | |
39 | #error "unsupported architecture" | |
40 | #endif // defined(__i386__) || defined(__x86_64__) | |
41 | return flavor; | |
42 | } | |
43 | ||
44 | static exception_mask_t | |
45 | _exception_mask(os_crash_flags_t flags) | |
46 | { | |
47 | exception_mask_t mask = 0; | |
48 | if (flags & OS_CRASH_FLAG_CRASH) { | |
49 | mask |= EXC_MASK_CRASH; | |
50 | } | |
51 | if (flags & OS_CRASH_FLAG_GUARD) { | |
52 | mask |= EXC_MASK_GUARD; | |
53 | } | |
54 | if (flags & OS_CRASH_FLAG_RESOURCE) { | |
55 | mask |= EXC_MASK_RESOURCE; | |
56 | } | |
57 | if (flags & OS_CRASH_FLAG_CORPSE) { | |
58 | mask |= EXC_MASK_CORPSE_NOTIFY; | |
59 | mask &= (exception_mask_t)(~EXC_MASK_CRASH); | |
60 | } | |
61 | return mask; | |
62 | } | |
63 | ||
64 | static exception_behavior_t | |
65 | _exception_behavior(void) | |
66 | { | |
67 | return (EXCEPTION_STATE_IDENTITY|MACH_EXCEPTION_CODES); | |
68 | } | |
69 | ||
70 | static void | |
71 | _os_exception_port_set(os_crash_port_t *ep, | |
72 | os_crash_type_t type, mach_port_t p) | |
73 | { | |
74 | kern_return_t kr = KERN_FAILURE; | |
75 | ||
76 | ep->oep_type = type; | |
77 | ep->oep_port = p; | |
78 | ||
79 | kr = mach_port_mod_refs(mach_task_self(), p, MACH_PORT_RIGHT_SEND, 1); | |
80 | if (kr == KERN_SUCCESS) { | |
81 | ep->oep_port = p; | |
82 | } else { | |
83 | ep->oep_port = MACH_PORT_DEAD; | |
84 | } | |
85 | } | |
86 | ||
87 | #pragma mark API | |
88 | kern_return_t | |
89 | os_crash_set_reporter_port(mach_port_t where, | |
90 | os_crash_flags_t flags, mach_port_t p) | |
91 | { | |
92 | kern_return_t kr = KERN_FAILURE; | |
93 | exception_mask_t mask = _exception_mask(flags); | |
94 | exception_behavior_t bhvr = _exception_behavior(); | |
95 | thread_state_flavor_t flvr = _exception_thread_state_flavor(); | |
96 | mach_port_t host_priv = MACH_PORT_NULL; | |
97 | ||
98 | // If we're not privleged to get the host-privileged port, no big deal, then | |
99 | // the comparison below will fail. | |
100 | (void)host_get_host_priv_port(mach_host_self(), &host_priv); | |
101 | ||
102 | if (flags == OS_CRASH_FLAG_INIT) { | |
103 | return KERN_SUCCESS; | |
104 | } | |
105 | ||
106 | if (where == mach_host_self() || where == host_priv) { | |
107 | kr = host_set_exception_ports(where, mask, p, bhvr, flvr); | |
108 | } else { | |
109 | kr = task_set_exception_ports(where, mask, p, bhvr, flvr); | |
110 | } | |
111 | ||
112 | return kr; | |
113 | } | |
114 | ||
115 | kern_return_t | |
116 | os_crash_get_reporter_port_array(mach_port_t where, os_crash_port_array_t array) | |
117 | { | |
118 | kern_return_t kr = KERN_FAILURE; | |
119 | exception_mask_t masks[EXC_TYPES_COUNT]; | |
120 | mach_msg_type_number_t nmasks = 0; | |
121 | exception_port_t ports[EXC_TYPES_COUNT]; | |
122 | exception_behavior_t behaviors[EXC_TYPES_COUNT]; | |
123 | thread_state_flavor_t flavors[EXC_TYPES_COUNT]; | |
124 | exception_mask_t mask = EXC_MASK_CRASH | |
125 | | EXC_MASK_GUARD | |
126 | | EXC_MASK_RESOURCE | |
127 | | EXC_MASK_CORPSE_NOTIFY; | |
128 | size_t i = 0; | |
129 | size_t j = 0; | |
130 | mach_port_t host_priv = MACH_PORT_NULL; | |
131 | ||
132 | (void)host_get_host_priv_port(mach_host_self(), &host_priv); | |
133 | ||
134 | if (where == mach_host_self() || where == host_priv) { | |
135 | kr = host_get_exception_ports(mach_host_self(), mask, | |
136 | masks, &nmasks, ports, behaviors, flavors); | |
137 | } else { | |
138 | kr = task_get_exception_ports(where, mask, | |
139 | masks, &nmasks, ports, behaviors, flavors); | |
140 | } | |
141 | ||
142 | if (kr) { | |
143 | goto __out; | |
144 | } | |
145 | ||
146 | bzero(array, sizeof(array[0]) * OS_CRASH_PORT_ARRAY_COUNT); | |
147 | for (i = 0; i < nmasks; i++) { | |
148 | if (masks[i] & EXC_MASK_CRASH) { | |
149 | _os_exception_port_set(&array[j], OS_CRASH_CRASH, ports[i]); | |
150 | j++; | |
151 | } | |
152 | if (masks[i] & EXC_MASK_GUARD) { | |
153 | _os_exception_port_set(&array[j], OS_CRASH_GUARD, ports[i]); | |
154 | j++; | |
155 | } | |
156 | if (masks[i] & EXC_MASK_RESOURCE) { | |
157 | _os_exception_port_set(&array[j], OS_CRASH_RESOURCE, ports[i]); | |
158 | j++; | |
159 | } | |
160 | if (masks[i] & EXC_MASK_CORPSE_NOTIFY) { | |
161 | _os_exception_port_set(&array[j], OS_CRASH_CORPSE, ports[i]); | |
162 | j++; | |
163 | } | |
164 | kr = mach_port_deallocate(mach_task_self(), ports[i]); | |
165 | os_assert_mach("deallocate port", kr); | |
166 | } | |
167 | ||
168 | __out: | |
169 | return kr; | |
170 | } | |
171 | ||
172 | void | |
173 | os_crash_port_array_deallocate(os_crash_port_array_t array) | |
174 | { | |
175 | size_t i = 0; | |
176 | ||
177 | for (i = 0; i < 16; i++) { | |
178 | if (MACH_PORT_VALID(array[i].oep_port)) { | |
179 | kern_return_t kr = KERN_FAILURE; | |
180 | kr = mach_port_deallocate(mach_task_self(), array[i].oep_port); | |
181 | os_assert_mach("deallocate port", kr); | |
182 | } | |
183 | } | |
184 | } | |
185 | ||
186 | kern_return_t | |
187 | os_crash_spawnattr_set_reporter_port(posix_spawnattr_t *psattr, | |
188 | os_crash_flags_t flags, mach_port_t p) | |
189 | { | |
190 | int error = -1; | |
191 | exception_mask_t mask = _exception_mask(flags); | |
192 | exception_behavior_t bhvr = _exception_behavior(); | |
193 | thread_state_flavor_t flvr = _exception_thread_state_flavor(); | |
194 | ||
195 | if (!MACH_PORT_VALID(p)) { | |
196 | return KERN_INVALID_NAME; | |
197 | } | |
198 | ||
199 | if (flags == OS_CRASH_FLAG_INIT) { | |
200 | return KERN_SUCCESS; | |
201 | } | |
202 | ||
203 | error = posix_spawnattr_setexceptionports_np(psattr, mask, p, bhvr, flvr); | |
204 | os_assert_zero(error); | |
205 | ||
206 | return KERN_SUCCESS; | |
207 | } |