1/*
2 * Copyright (C) 2017 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include <wtf/threads/Signals.h>
28
29#if USE(PTHREADS) && HAVE(MACHINE_CONTEXT)
30
31#if HAVE(MACH_EXCEPTIONS)
32extern "C" {
33#include "MachExceptionsServer.h"
34};
35#endif
36
37#include <cstdio>
38#include <mutex>
39#include <signal.h>
40
41#if HAVE(MACH_EXCEPTIONS)
42#include <dispatch/dispatch.h>
43#include <mach/mach.h>
44#include <mach/thread_act.h>
45#endif
46
47#include <wtf/Atomics.h>
48#include <wtf/DataLog.h>
49#include <wtf/LocklessBag.h>
50#include <wtf/NeverDestroyed.h>
51#include <wtf/ThreadGroup.h>
52#include <wtf/ThreadMessage.h>
53#include <wtf/Threading.h>
54
55
56namespace WTF {
57
58
59static LazyNeverDestroyed<LocklessBag<SignalHandler>> handlers[static_cast<size_t>(Signal::NumberOfSignals)] = { };
60static std::once_flag initializeOnceFlags[static_cast<size_t>(Signal::NumberOfSignals)];
61static struct sigaction oldActions[static_cast<size_t>(Signal::NumberOfSignals)];
62
63#if HAVE(MACH_EXCEPTIONS)
64// You can read more about mach exceptions here:
65// http://www.cs.cmu.edu/afs/cs/project/mach/public/doc/unpublished/exception.ps
66// and the Mach interface Generator (MiG) here:
67// http://www.cs.cmu.edu/afs/cs/project/mach/public/doc/unpublished/mig.ps
68
69static mach_port_t exceptionPort;
70static constexpr size_t maxMessageSize = 1 * KB;
71
72static void startMachExceptionHandlerThread()
73{
74 static std::once_flag once;
75 std::call_once(once, [] {
76 kern_return_t kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &exceptionPort);
77 RELEASE_ASSERT(kr == KERN_SUCCESS);
78 kr = mach_port_insert_right(mach_task_self(), exceptionPort, exceptionPort, MACH_MSG_TYPE_MAKE_SEND);
79 RELEASE_ASSERT(kr == KERN_SUCCESS);
80
81 dispatch_source_t source = dispatch_source_create(
82 DISPATCH_SOURCE_TYPE_MACH_RECV, exceptionPort, 0, DISPATCH_TARGET_QUEUE_DEFAULT);
83 RELEASE_ASSERT(source);
84
85 dispatch_source_set_event_handler(source, ^{
86 UNUSED_PARAM(source); // Capture a pointer to source in user space to silence the leaks tool.
87
88 kern_return_t kr = mach_msg_server_once(
89 mach_exc_server, maxMessageSize, exceptionPort, MACH_MSG_TIMEOUT_NONE);
90 RELEASE_ASSERT(kr == KERN_SUCCESS);
91 });
92
93 // No need for a cancel handler because we never destroy exceptionPort.
94
95 dispatch_resume(source);
96 });
97}
98
99static Signal fromMachException(exception_type_t type)
100{
101 switch (type) {
102 case EXC_BAD_ACCESS: return Signal::BadAccess;
103 case EXC_BAD_INSTRUCTION: return Signal::Ill;
104 default: break;
105 }
106 return Signal::Unknown;
107}
108
109static exception_mask_t toMachMask(Signal signal)
110{
111 switch (signal) {
112 case Signal::BadAccess: return EXC_MASK_BAD_ACCESS;
113 case Signal::Ill: return EXC_MASK_BAD_INSTRUCTION;
114 default: break;
115 }
116 RELEASE_ASSERT_NOT_REACHED();
117}
118
119extern "C" {
120
121// We need to implement stubs for catch_mach_exception_raise and catch_mach_exception_raise_state_identity.
122// The MiG generated file will fail to link otherwise, even though we don't use the functions. Only the
123// catch_mach_exception_raise_state function should be called because we pass EXCEPTION_STATE to
124// thread_set_exception_ports.
125kern_return_t catch_mach_exception_raise(mach_port_t, mach_port_t, mach_port_t, exception_type_t, mach_exception_data_t, mach_msg_type_number_t)
126{
127 dataLogLn("We should not have called catch_exception_raise(), please file a bug at bugs.webkit.org");
128 return KERN_FAILURE;
129}
130
131kern_return_t catch_mach_exception_raise_state_identity(mach_port_t, mach_port_t, mach_port_t, exception_type_t, mach_exception_data_t, mach_msg_type_number_t, int*, thread_state_t, mach_msg_type_number_t, thread_state_t, mach_msg_type_number_t*)
132{
133 dataLogLn("We should not have called catch_mach_exception_raise_state_identity, please file a bug at bugs.webkit.org");
134 return KERN_FAILURE;
135}
136
137kern_return_t catch_mach_exception_raise_state(
138 mach_port_t port,
139 exception_type_t exceptionType,
140 const mach_exception_data_t exceptionData,
141 mach_msg_type_number_t dataCount,
142 int* stateFlavor,
143 const thread_state_t inState,
144 mach_msg_type_number_t inStateCount,
145 thread_state_t outState,
146 mach_msg_type_number_t* outStateCount)
147{
148 RELEASE_ASSERT(port == exceptionPort);
149 // If we wanted to distinguish between SIGBUS and SIGSEGV for EXC_BAD_ACCESS on Darwin we could do:
150 // if (exceptionData[0] == KERN_INVALID_ADDRESS)
151 // signal = SIGSEGV;
152 // else
153 // signal = SIGBUS;
154 Signal signal = fromMachException(exceptionType);
155 RELEASE_ASSERT(signal != Signal::Unknown);
156
157 memcpy(outState, inState, inStateCount * sizeof(inState[0]));
158 *outStateCount = inStateCount;
159
160#if CPU(X86_64)
161 RELEASE_ASSERT(*stateFlavor == x86_THREAD_STATE);
162 PlatformRegisters& registers = reinterpret_cast<x86_thread_state_t*>(outState)->uts.ts64;
163#elif CPU(X86)
164 RELEASE_ASSERT(*stateFlavor == x86_THREAD_STATE);
165 PlatformRegisters& registers = reinterpret_cast<x86_thread_state_t*>(outState)->uts.ts32;
166#elif CPU(ARM64)
167 RELEASE_ASSERT(*stateFlavor == ARM_THREAD_STATE);
168 PlatformRegisters& registers = reinterpret_cast<arm_unified_thread_state*>(outState)->ts_64;
169#elif CPU(ARM)
170 RELEASE_ASSERT(*stateFlavor == ARM_THREAD_STATE);
171 PlatformRegisters& registers = reinterpret_cast<arm_unified_thread_state*>(outState)->ts_32;
172#endif
173
174 SigInfo info;
175 if (signal == Signal::BadAccess) {
176 ASSERT_UNUSED(dataCount, dataCount == 2);
177 info.faultingAddress = reinterpret_cast<void*>(exceptionData[1]);
178 }
179
180 bool didHandle = false;
181 handlers[static_cast<size_t>(signal)]->iterate([&] (const SignalHandler& handler) {
182 SignalAction handlerResult = handler(signal, info, registers);
183 didHandle |= handlerResult == SignalAction::Handled;
184 });
185
186 if (didHandle)
187 return KERN_SUCCESS;
188 return KERN_FAILURE;
189}
190
191};
192
193static bool useMach { false };
194void handleSignalsWithMach()
195{
196 useMach = true;
197}
198
199
200exception_mask_t activeExceptions { 0 };
201
202inline void setExceptionPorts(const AbstractLocker& threadGroupLocker, Thread& thread)
203{
204 UNUSED_PARAM(threadGroupLocker);
205 kern_return_t result = thread_set_exception_ports(thread.machThread(), activeExceptions, exceptionPort, EXCEPTION_STATE | MACH_EXCEPTION_CODES, MACHINE_THREAD_STATE);
206 if (result != KERN_SUCCESS) {
207 dataLogLn("thread set port failed due to ", mach_error_string(result));
208 CRASH();
209 }
210}
211
212static ThreadGroup& activeThreads()
213{
214 static std::once_flag initializeKey;
215 static ThreadGroup* activeThreadsPtr = nullptr;
216 std::call_once(initializeKey, [&] {
217 static NeverDestroyed<std::shared_ptr<ThreadGroup>> activeThreads { ThreadGroup::create() };
218 activeThreadsPtr = activeThreads.get().get();
219 });
220 return *activeThreadsPtr;
221}
222
223void registerThreadForMachExceptionHandling(Thread& thread)
224{
225 auto locker = holdLock(activeThreads().getLock());
226 if (activeThreads().add(locker, thread) == ThreadGroupAddResult::NewlyAdded)
227 setExceptionPorts(locker, thread);
228}
229
230#else
231static constexpr bool useMach = false;
232#endif // HAVE(MACH_EXCEPTIONS)
233
234
235inline size_t offsetForSystemSignal(int sig)
236{
237 Signal signal = fromSystemSignal(sig);
238 return static_cast<size_t>(signal) + (sig == SIGBUS);
239}
240
241static void jscSignalHandler(int, siginfo_t*, void*);
242
243void installSignalHandler(Signal signal, SignalHandler&& handler)
244{
245 ASSERT(signal < Signal::Unknown);
246#if HAVE(MACH_EXCEPTIONS)
247 ASSERT(!useMach || signal != Signal::Usr);
248
249 if (useMach)
250 startMachExceptionHandlerThread();
251#endif
252
253 std::call_once(initializeOnceFlags[static_cast<size_t>(signal)], [&] {
254 handlers[static_cast<size_t>(signal)].construct();
255
256 if (!useMach) {
257 struct sigaction action;
258 action.sa_sigaction = jscSignalHandler;
259 auto result = sigfillset(&action.sa_mask);
260 RELEASE_ASSERT(!result);
261 // Do not block this signal since it is used on non-Darwin systems to suspend and resume threads.
262 result = sigdelset(&action.sa_mask, SigThreadSuspendResume);
263 RELEASE_ASSERT(!result);
264 action.sa_flags = SA_SIGINFO;
265 auto systemSignals = toSystemSignal(signal);
266 result = sigaction(std::get<0>(systemSignals), &action, &oldActions[offsetForSystemSignal(std::get<0>(systemSignals))]);
267 if (std::get<1>(systemSignals))
268 result |= sigaction(*std::get<1>(systemSignals), &action, &oldActions[offsetForSystemSignal(*std::get<1>(systemSignals))]);
269 RELEASE_ASSERT(!result);
270 }
271
272 });
273
274 handlers[static_cast<size_t>(signal)]->add(WTFMove(handler));
275
276#if HAVE(MACH_EXCEPTIONS)
277 auto locker = holdLock(activeThreads().getLock());
278 if (useMach) {
279 activeExceptions |= toMachMask(signal);
280
281 for (auto& thread : activeThreads().threads(locker))
282 setExceptionPorts(locker, thread.get());
283 }
284#endif
285}
286
287void jscSignalHandler(int sig, siginfo_t* info, void* ucontext)
288{
289 Signal signal = fromSystemSignal(sig);
290
291 auto restoreDefault = [&] {
292 struct sigaction defaultAction;
293 defaultAction.sa_handler = SIG_DFL;
294 sigfillset(&defaultAction.sa_mask);
295 defaultAction.sa_flags = 0;
296 auto result = sigaction(sig, &defaultAction, nullptr);
297 dataLogLnIf(result == -1, "Unable to restore the default handler while proccessing signal ", sig, " the process is probably deadlocked. (errno: ", strerror(errno), ")");
298 };
299
300 // This shouldn't happen but we might as well be careful.
301 if (signal == Signal::Unknown) {
302 dataLogLn("We somehow got called for an unknown signal ", sig, ", halp.");
303 restoreDefault();
304 return;
305 }
306
307 SigInfo sigInfo;
308 if (signal == Signal::BadAccess)
309 sigInfo.faultingAddress = info->si_addr;
310
311 PlatformRegisters& registers = registersFromUContext(reinterpret_cast<ucontext_t*>(ucontext));
312
313 bool didHandle = false;
314 bool restoreDefaultHandler = false;
315 handlers[static_cast<size_t>(signal)]->iterate([&] (const SignalHandler& handler) {
316 switch (handler(signal, sigInfo, registers)) {
317 case SignalAction::Handled:
318 didHandle = true;
319 break;
320 case SignalAction::ForceDefault:
321 restoreDefaultHandler = true;
322 break;
323 default:
324 break;
325 }
326 });
327
328 if (restoreDefaultHandler) {
329 restoreDefault();
330 return;
331 }
332
333 unsigned oldActionIndex = static_cast<size_t>(signal) + (sig == SIGBUS);
334 struct sigaction& oldAction = oldActions[static_cast<size_t>(oldActionIndex)];
335 if (signal == Signal::Usr) {
336 if (oldAction.sa_sigaction)
337 oldAction.sa_sigaction(sig, info, ucontext);
338 return;
339 }
340
341 if (!didHandle) {
342 if (oldAction.sa_sigaction) {
343 oldAction.sa_sigaction(sig, info, ucontext);
344 return;
345 }
346
347 restoreDefault();
348 return;
349 }
350}
351
352} // namespace WTF
353
354#endif // USE(PTHREADS) && HAVE(MACHINE_CONTEXT)
355