Reland r216808, underlying lldb bug has been fixed.
[WebKit-https.git] / Source / WTF / wtf / threads / Signals.cpp
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 "Signals.h"
28
29 #if USE(PTHREADS)
30
31 #if HAVE(MACH_EXCEPTIONS)
32 extern "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 <mach/mach.h>
43 #include <mach/thread_act.h>
44 #endif
45
46 #include <wtf/Atomics.h>
47 #include <wtf/DataLog.h>
48 #include <wtf/HashSet.h>
49 #include <wtf/LocklessBag.h>
50 #include <wtf/NeverDestroyed.h>
51 #include <wtf/ThreadMessage.h>
52 #include <wtf/Threading.h>
53
54
55 namespace WTF {
56
57     
58 static LazyNeverDestroyed<LocklessBag<SignalHandler>> handlers[static_cast<size_t>(Signal::NumberOfSignals)] = { };
59 static std::once_flag initializeOnceFlags[static_cast<size_t>(Signal::NumberOfSignals)];
60 static struct sigaction oldActions[static_cast<size_t>(Signal::NumberOfSignals)];
61
62 #if HAVE(MACH_EXCEPTIONS)
63 // You can read more about mach exceptions here:
64 // http://www.cs.cmu.edu/afs/cs/project/mach/public/doc/unpublished/exception.ps
65 // and the Mach interface Generator (MiG) here:
66 // http://www.cs.cmu.edu/afs/cs/project/mach/public/doc/unpublished/mig.ps
67
68 static mach_port_t exceptionPort;
69 static constexpr size_t maxMessageSize = 1 * KB;
70
71 static void startMachExceptionHandlerThread()
72 {
73     static std::once_flag once;
74     std::call_once(once, [] {
75         if (mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &exceptionPort) != KERN_SUCCESS)
76             CRASH();
77
78         if (mach_port_insert_right(mach_task_self(), exceptionPort, exceptionPort, MACH_MSG_TYPE_MAKE_SEND) != KERN_SUCCESS)
79             CRASH();
80
81         // FIXME: This should use a dispatch queue.
82         // See: https://bugs.webkit.org/show_bug.cgi?id=172003
83         (void)Thread::create(
84             "WTF Mach Exception Thread", [] () {
85                 union Message {
86                     mach_msg_header_t header;
87                     char data[maxMessageSize];
88                 };
89                 Message messageHeaderIn;
90                 Message messageHeaderOut;
91
92                 while (true) {
93                     kern_return_t messageResult = mach_msg(&messageHeaderIn.header, MACH_RCV_MSG, 0, maxMessageSize, exceptionPort, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
94                     if (messageResult == KERN_SUCCESS) {
95                         if (!mach_exc_server(&messageHeaderIn.header, &messageHeaderOut.header))
96                             CRASH();
97
98                         messageResult = mach_msg(&messageHeaderOut.header, MACH_SEND_MSG, messageHeaderOut.header.msgh_size, 0, messageHeaderOut.header.msgh_local_port, 0, MACH_PORT_NULL);
99                         RELEASE_ASSERT(messageResult == KERN_SUCCESS);
100                     } else {
101                         dataLogLn("Failed to receive mach message due to ", mach_error_string(messageResult));
102                         RELEASE_ASSERT_NOT_REACHED();
103                     }
104                 }
105         }).leakRef();
106     });
107 }
108
109 static Signal fromMachException(exception_type_t type)
110 {
111     switch (type) {
112     case EXC_BAD_ACCESS: return Signal::SegV;
113     case EXC_BAD_INSTRUCTION: return Signal::Ill;
114     case EXC_BREAKPOINT: return Signal::Trap;
115     default: break;
116     }
117     return Signal::Unknown;
118 }
119
120 static exception_mask_t toMachMask(Signal signal)
121 {
122     switch (signal) {
123     case Signal::SegV: return EXC_MASK_BAD_ACCESS;
124     case Signal::Ill: return EXC_MASK_BAD_INSTRUCTION;
125     case Signal::Trap: return EXC_MASK_BREAKPOINT;
126     default: break;
127     }
128     RELEASE_ASSERT_NOT_REACHED();
129 }
130
131 extern "C" {
132
133 // We need to implement stubs for catch_mach_exception_raise and catch_mach_exception_raise_state_identity.
134 // The MiG generated file will fail to link otherwise, even though we don't use the functions. Only the
135 // catch_mach_exception_raise_state function should be called because we pass EXCEPTION_STATE to
136 // thread_set_exception_ports.
137 kern_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)
138 {
139     dataLogLn("We should not have called catch_exception_raise(), please file a bug at bugs.webkit.org");
140     return KERN_FAILURE;
141 }
142
143 kern_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*)
144 {
145     dataLogLn("We should not have called catch_mach_exception_raise_state_identity, please file a bug at bugs.webkit.org");
146     return KERN_FAILURE;
147 }
148
149 kern_return_t catch_mach_exception_raise_state(
150     mach_port_t port,
151     exception_type_t exceptionType,
152     const mach_exception_data_t exceptionData,
153     mach_msg_type_number_t dataCount,
154     int* stateFlavor,
155     const thread_state_t inState,
156     mach_msg_type_number_t inStateCount,
157     thread_state_t outState,
158     mach_msg_type_number_t* outStateCount)
159 {
160     RELEASE_ASSERT(port == exceptionPort);
161     Signal signal = fromMachException(exceptionType);
162     RELEASE_ASSERT(signal != Signal::Unknown);
163
164     memcpy(outState, inState, inStateCount * sizeof(inState[0]));
165     *outStateCount = inStateCount;
166
167 #if CPU(X86_64)
168     RELEASE_ASSERT(*stateFlavor == x86_THREAD_STATE);
169     PlatformRegisters& registers = reinterpret_cast<x86_thread_state_t*>(outState)->uts.ts64;
170 #elif CPU(X86)
171     RELEASE_ASSERT(*stateFlavor == x86_THREAD_STATE);
172     PlatformRegisters& registers = reinterpret_cast<x86_thread_state_t*>(outState)->uts.ts32;
173 #elif CPU(ARM64)
174     RELEASE_ASSERT(*stateFlavor == ARM_THREAD_STATE);
175     PlatformRegisters& registers = reinterpret_cast<arm_unified_thread_state*>(outState)->ts_64;
176 #elif CPU(ARM)
177     RELEASE_ASSERT(*stateFlavor == ARM_THREAD_STATE);
178     PlatformRegisters& registers = reinterpret_cast<arm_unified_thread_state*>(outState)->ts_32;
179 #endif
180
181     SigInfo info;
182     if (signal == Signal::SegV) {
183         ASSERT_UNUSED(dataCount, dataCount == 2);
184         info.faultingAddress = reinterpret_cast<void*>(exceptionData[1]);
185     }
186
187     bool didHandle = false;
188     handlers[static_cast<size_t>(signal)]->iterate([&] (const SignalHandler& handler) {
189         SignalAction handlerResult = handler(signal, info, registers);
190         didHandle |= handlerResult == SignalAction::Handled;
191     });
192
193     if (didHandle)
194         return KERN_SUCCESS;
195     return KERN_FAILURE;
196 }
197
198 };
199
200 static bool useMach { false };
201 void handleSignalsWithMach()
202 {
203     deliverMessagesUsingMach();
204     useMach = true;
205 }
206
207
208 static StaticLock threadLock;
209 exception_mask_t activeExceptions { 0 };
210
211 inline void setExceptionPorts(const AbstractLocker&, Thread* thread)
212 {
213     kern_return_t result = thread_set_exception_ports(thread->machThread(), activeExceptions, exceptionPort, EXCEPTION_STATE | MACH_EXCEPTION_CODES, MACHINE_THREAD_STATE);
214     if (result != KERN_SUCCESS) {
215         dataLogLn("thread set port failed due to ", mach_error_string(result));
216         CRASH();
217     }
218 }
219
220 inline HashSet<Thread*>& activeThreads(const AbstractLocker&)
221 {
222     static NeverDestroyed<HashSet<Thread*>> activeThreads;
223     return activeThreads;
224 }
225
226 void registerThreadForMachExceptionHandling(Thread* thread)
227 {
228     auto locker = holdLock(threadLock);
229     auto result = activeThreads(locker).add(thread);
230
231     if (result.isNewEntry)
232         setExceptionPorts(locker, thread);
233 }
234
235 void unregisterThreadForMachExceptionHandling(Thread* thread)
236 {
237     auto locker = holdLock(threadLock);
238     activeThreads(locker).remove(thread);
239 }
240
241 #else
242 static constexpr bool useMach = false;
243 #endif // HAVE(MACH_EXCEPTIONS)
244
245 static void jscSignalHandler(int, siginfo_t*, void*);
246
247 void installSignalHandler(Signal signal, SignalHandler&& handler)
248 {
249     ASSERT(signal < Signal::Unknown);
250 #if HAVE(MACH_EXCEPTIONS)
251     // Since mach only has EXC_BAD_ACCESS, which covers both SegV and Bus, we arbitarily choose to make
252     // mach EXC_BAD_ACCESSes map to SegV.
253     // FIXME: We should just use a single Signal::BadAccess value instead of SegV/Bus.
254     // See: https://bugs.webkit.org/show_bug.cgi?id=172063
255     if (signal == Signal::Bus && useMach)
256         return;
257     ASSERT(!useMach || signal != Signal::Usr);
258
259     if (useMach)
260         startMachExceptionHandlerThread();
261 #endif
262
263     std::call_once(initializeOnceFlags[static_cast<size_t>(signal)], [&] {
264         handlers[static_cast<size_t>(signal)].construct();
265
266         if (!useMach) {
267             struct sigaction action;
268             action.sa_sigaction = jscSignalHandler;
269             auto result = sigfillset(&action.sa_mask);
270             RELEASE_ASSERT(!result);
271             action.sa_flags = SA_SIGINFO;
272             result = sigaction(toSystemSignal(signal), &action, &oldActions[static_cast<size_t>(signal)]);
273             RELEASE_ASSERT(!result);
274         }
275
276     });
277
278     handlers[static_cast<size_t>(signal)]->add(WTFMove(handler));
279
280 #if HAVE(MACH_EXCEPTIONS)
281     auto locker = holdLock(threadLock);
282     if (useMach) {
283         activeExceptions |= toMachMask(signal);
284
285         for (Thread* thread : activeThreads(locker))
286             setExceptionPorts(locker, thread);
287     }
288 #endif
289 }
290
291 void jscSignalHandler(int sig, siginfo_t* info, void* ucontext)
292 {
293     Signal signal = fromSystemSignal(sig);
294
295     auto restoreDefault = [&] {
296         struct sigaction defaultAction;
297         defaultAction.sa_handler = SIG_DFL;
298         sigfillset(&defaultAction.sa_mask);
299         defaultAction.sa_flags = 0;
300         auto result = sigaction(sig, &defaultAction, nullptr);
301         dataLogLnIf(result == -1, "Unable to restore the default handler while proccessing signal ", sig, " the process is probably deadlocked. (errno: ", strerror(errno), ")");
302     };
303
304     // This shouldn't happen but we might as well be careful.
305     if (signal == Signal::Unknown) {
306         dataLogLn("We somehow got called for an unknown signal ", sig, ", halp.");
307         restoreDefault();
308         return;
309     }
310
311     SigInfo sigInfo;
312     if (signal == Signal::SegV || signal == Signal::Bus)
313         sigInfo.faultingAddress = info->si_addr;
314
315     PlatformRegisters& registers = registersFromUContext(reinterpret_cast<ucontext_t*>(ucontext));
316
317     bool didHandle = false;
318     bool restoreDefaultHandler = false;
319     handlers[static_cast<size_t>(signal)]->iterate([&] (const SignalHandler& handler) {
320         switch (handler(signal, sigInfo, registers)) {
321         case SignalAction::Handled:
322             didHandle = true;
323             break;
324         case SignalAction::ForceDefault:
325             restoreDefaultHandler = true;
326             break;
327         default:
328             break;
329         }
330     });
331
332     if (restoreDefaultHandler) {
333         restoreDefault();
334         return;
335     }
336
337     struct sigaction& oldAction = oldActions[static_cast<size_t>(signal)];
338     if (signal == Signal::Usr) {
339         if (oldAction.sa_sigaction)
340             oldAction.sa_sigaction(sig, info, ucontext);
341         return;
342     }
343
344     if (!didHandle) {
345         if (oldAction.sa_sigaction) {
346             oldAction.sa_sigaction(sig, info, ucontext);
347             return;
348         }
349
350         restoreDefault();
351         return;
352     }
353 }
354
355 } // namespace WTF
356
357 #endif // USE(PTHREADS)