Changeset 216876 in webkit
- Timestamp:
- May 15, 2017, 1:34:13 PM (8 years ago)
- Location:
- trunk
- Files:
-
- 15 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/LayoutTests/ChangeLog
r216874 r216876 1 2017-05-15 Mark Lam <mark.lam@apple.com> 2 3 WorkerRunLoop::Task::performTask() should check !scriptController->isTerminatingExecution(). 4 https://bugs.webkit.org/show_bug.cgi?id=171775 5 <rdar://problem/30975761> 6 7 Reviewed by Filip Pizlo. 8 9 * TestExpectations: 10 1 11 2017-05-15 Myles C. Maxfield <mmaxfield@apple.com> 2 12 -
trunk/LayoutTests/TestExpectations
r216834 r216876 364 364 365 365 webkit.org/b/161312 imported/w3c/web-platform-tests/html/semantics/document-metadata/the-link-element/document-without-browsing-context.html [ Failure Pass ] 366 367 # rdar://problem/30975761368 [ Debug ] http/tests/fetch/fetch-in-worker-crash.html [ Skip ]369 366 370 367 imported/w3c/web-platform-tests/XMLHttpRequest/send-conditional-cors.htm [ Failure ] -
trunk/Source/JavaScriptCore/ChangeLog
r216837 r216876 1 2017-05-15 Mark Lam <mark.lam@apple.com> 2 3 WorkerRunLoop::Task::performTask() should check !scriptController->isTerminatingExecution(). 4 https://bugs.webkit.org/show_bug.cgi?id=171775 5 <rdar://problem/30975761> 6 7 Reviewed by Filip Pizlo. 8 9 Increased the number of frames captured in VM::nativeStackTraceOfLastThrow() 10 from 25 to 100. From experience, I found that 25 is sometimes not sufficient 11 for our debugging needs. 12 13 Also added VM::throwingThread() to track which thread an exception was thrown in. 14 This may be useful if the client is entering the VM from different threads. 15 16 * runtime/ExceptionScope.cpp: 17 (JSC::ExceptionScope::unexpectedExceptionMessage): 18 * runtime/ExceptionScope.h: 19 (JSC::ExceptionScope::exception): 20 (JSC::ExceptionScope::unexpectedExceptionMessage): 21 * runtime/Options.h: 22 - Added the unexpectedExceptionStackTraceLimit option. 23 * runtime/VM.cpp: 24 (JSC::VM::throwException): 25 * runtime/VM.h: 26 (JSC::VM::throwingThread): 27 (JSC::VM::clearException): 28 1 29 2017-05-13 David Kilzer <ddkilzer@apple.com> 2 30 -
trunk/Source/JavaScriptCore/runtime/ExceptionScope.cpp
r216826 r216876 1 1 /* 2 * Copyright (C) 2016 Apple Inc. All rights reserved.2 * Copyright (C) 2016-2017 Apple Inc. All rights reserved. 3 3 * 4 4 * Redistribution and use in source and binary forms, with or without … … 28 28 29 29 #include "Exception.h" 30 #include "ExceptionHelpers.h" 30 31 #include <wtf/StackTrace.h> 31 32 #include <wtf/StringPrintStream.h> 33 #include <wtf/Threading.h> 32 34 33 35 namespace JSC { … … 54 56 StringPrintStream out; 55 57 56 out.println("Unexpected exception observed at:");57 auto currentStack = std::unique_ptr<StackTrace>(StackTrace::captureStackTrace( 25, 1));58 out.println("Unexpected exception observed on thread ", currentThread(), " at:"); 59 auto currentStack = std::unique_ptr<StackTrace>(StackTrace::captureStackTrace(Options::unexpectedExceptionStackTraceLimit(), 1)); 58 60 currentStack->dump(out, " "); 59 61 … … 61 63 return CString(); 62 64 63 out.println("The exception was thrown from :");65 out.println("The exception was thrown from thread ", m_vm.throwingThread(), " at:"); 64 66 m_vm.nativeStackTraceOfLastThrow()->dump(out, " "); 65 67 -
trunk/Source/JavaScriptCore/runtime/ExceptionScope.h
r216826 r216876 1 1 /* 2 * Copyright (C) 2016 Apple Inc. All rights reserved.2 * Copyright (C) 2016-2017 Apple Inc. All rights reserved. 3 3 * 4 4 * Redistribution and use in source and binary forms, with or without … … 63 63 ALWAYS_INLINE VM& vm() const { return m_vm; } 64 64 ALWAYS_INLINE Exception* exception() { return m_vm.exception(); } 65 ALWAYS_INLINE CString unexpectedExceptionMessage() { return { }; }66 65 67 66 ALWAYS_INLINE void assertNoException() { ASSERT(!exception()); } … … 74 73 ExceptionScope(const ExceptionScope&) = delete; 75 74 ExceptionScope(ExceptionScope&&) = default; 75 76 ALWAYS_INLINE CString unexpectedExceptionMessage() { return { }; } 76 77 77 78 VM& m_vm; -
trunk/Source/JavaScriptCore/runtime/Options.h
r216827 r216876 393 393 v(bool, dumpSimulatedThrows, false, Normal, "Dumps the call stack at each simulated throw for exception scope verification") \ 394 394 v(bool, validateExceptionChecks, false, Normal, "Verifies that needed exception checks are performed.") \ 395 v(unsigned, unexpectedExceptionStackTraceLimit, 100, Normal, "Stack trace limit for debugging unexpected exceptions observed in the VM") \ 395 396 \ 396 397 v(bool, useExecutableAllocationFuzz, false, Normal, nullptr) \ -
trunk/Source/JavaScriptCore/runtime/VM.cpp
r216826 r216876 614 614 615 615 #if ENABLE(EXCEPTION_SCOPE_VERIFICATION) 616 m_nativeStackTraceOfLastThrow = std::unique_ptr<StackTrace>(StackTrace::captureStackTrace(25)); 616 m_nativeStackTraceOfLastThrow = std::unique_ptr<StackTrace>(StackTrace::captureStackTrace(Options::unexpectedExceptionStackTraceLimit())); 617 m_throwingThread = currentThread(); 617 618 #endif 618 619 } -
trunk/Source/JavaScriptCore/runtime/VM.h
r216826 r216876 676 676 #if ENABLE(EXCEPTION_SCOPE_VERIFICATION) 677 677 StackTrace* nativeStackTraceOfLastThrow() const { return m_nativeStackTraceOfLastThrow.get(); } 678 ThreadIdentifier throwingThread() const { return m_throwingThread; } 678 679 #endif 679 680 … … 711 712 m_needExceptionCheck = false; 712 713 m_nativeStackTraceOfLastThrow = nullptr; 714 m_throwingThread = 0; 713 715 #endif 714 716 m_exception = nullptr; … … 758 760 mutable bool m_needExceptionCheck { false }; 759 761 std::unique_ptr<StackTrace> m_nativeStackTraceOfLastThrow; 762 ThreadIdentifier m_throwingThread; 760 763 #endif 761 764 -
trunk/Source/WebCore/ChangeLog
r216874 r216876 1 2017-05-15 Mark Lam <mark.lam@apple.com> 2 3 WorkerRunLoop::Task::performTask() should check !scriptController->isTerminatingExecution(). 4 https://bugs.webkit.org/show_bug.cgi?id=171775 5 <rdar://problem/30975761> 6 7 Reviewed by Filip Pizlo. 8 9 Currently, WorkerThread::stop() calls scheduleExecutionTermination() to terminate 10 JS execution first, followed by posting a cleanup task to the worker, and lastly, 11 it invokes terminate() on the WorkerRunLoop. 12 13 As a result, before the run loop is terminated, the worker thread may observe the 14 TerminatedExecutionException in JS code, bail out, see another JS task to run, 15 re-enters the VM to run said JS code, and fails with an assertion due to the 16 TerminatedExecutionException still being pending on VM entry. 17 18 WorkerRunLoop::Task::performTask() already has a check to only allow a task to 19 run if and only if !runLoop.terminated() and the task is not a clean up task. 20 We'll fix the above race by changing WorkerRunLoop::Task::performTask() to check 21 !context->script()->isTerminatingExecution() instead of !runLoop.terminated(). 22 Since WorkerThread::stop() always scheduleExecutionTermination() before it 23 terminates the run loop, !context->script()->isTerminatingExecution() implies 24 !runLoop.terminated(). 25 26 The only time that runLoop is terminated without scheduleExecutionTermination() 27 being called is when WorkerThread::stop() is called before the WorkerThread has 28 finished creating its WorkerGlobalScope. In this scenario, WorkerThread::stop() 29 will still terminate the run loop. Hence, after the WorkerGlobalScope is created 30 (in WorkerThread::workerThread()), we will check if the run loop has been 31 terminated (i.e. stop() was called). If so, we'll scheduleExecutionTermination() 32 there, and guarantee that if runloop.terminated() is true, then 33 context->script()->isTerminatingExecution() is also true. 34 35 Solutions that were considered but did not work (recorded for future reference): 36 37 1. In WorkerThread::stop(), call scheduleExecutionTermination() only after it 38 posts the cleanup task and terminate the run loop. 39 40 This did not work because this creates a race where the worker thread may run 41 the cleanup task before WorkerThread::stop() finishes. As a result, the 42 scriptController may be deleted before we get to invoke scheduleExecutionTermination() 43 on it, thereby resulting in a use after free. 44 45 To make this work, we would have to change the life cycle management strategy 46 of the WorkerScriptController. This is a more risky change that we would 47 want to take on at this time, and may also not be worth the gain. 48 49 2. Break scheduleExecutionTermination() up into 2 parts i.e. WorkerThread::stop() 50 will: 51 1. set the scriptControllers m_isTerminatingExecution flag before 52 posting the cleanup task and terminating the run loop, and 53 2. invoke VM::notifyNeedsTermination() after posting the cleanup task and 54 terminating the run loop. 55 56 This requires that we protect the liveness of the VM until we can invoke 57 notifyNeedsTermination() on it. 58 59 This did not work because: 60 1. We may end up destructing the VM in WorkerThread::stop() i.e. in the main 61 web frame, but only the worker thread holds the JS lock for the VM. 62 63 We can make the WorkerThread::stop() acquire the JS lock just before it 64 releases the protected VM's RefPtr, but that would mean the main thread 65 may be stuck waiting a bit for the worker thread to release its JSLock. 66 This is not desirable. 67 68 2. In practice, changing the liveness period of the Worker VM relative to its 69 WorkerScriptController and WorkerGlobalScope also has unexpected 70 ramifications. We observed many worker tests failing with assertion 71 failures and crashes due to this change. 72 73 Hence, this approach is also a more risky change than it appears on the 74 surface, and is not worth exploring at this time. 75 76 In the end, changing WorkerRunLoop::Task::performTask() to check for 77 !scriptController->isTerminatingExecution() is the most straight forward solution 78 that is easy to prove correct. 79 80 Also fixed a race in WorkerThread::workerThread() where it can delete the 81 WorkerGlobalScope while WorkerThread::stop() is in the midst of accessing it. 82 We now guard the the nullifying of m_workerGlobalScope with the 83 m_threadCreationAndWorkerGlobalScopeMutex as well. 84 85 UPDATE: the only new thing in this patch for re-landing (vs one previously landed) 86 is that instead of nullifying m_workerGlobalScope directly (thereby deleting the 87 WorkerGlobalScope context), we'll swap it out and delete it only after we've 88 unlocked the m_threadCreationAndWorkerGlobalScopeMutex. This is needed because 89 the destruction of the WorkerGlobalScope will cause the main thread to race against 90 the worker thread to delete the WorkerThread object, and the WorkerThread object 91 owns the mutex that we need to unlock after nullifying the m_workerGlobalScope 92 field. 93 94 This issue is covered by an existing test that I just unskipped in TestExpectations. 95 96 * bindings/js/JSDOMPromiseDeferred.cpp: 97 (WebCore::DeferredPromise::callFunction): 98 99 * bindings/js/WorkerScriptController.cpp: 100 (WebCore::WorkerScriptController::scheduleExecutionTermination): 101 - Added a check to do nothing and return early if the scriptController is already 102 terminating execution. 103 104 * workers/WorkerRunLoop.cpp: 105 (WebCore::WorkerRunLoop::runInMode): 106 (WebCore::WorkerRunLoop::runCleanupTasks): 107 (WebCore::WorkerRunLoop::Task::performTask): 108 109 * workers/WorkerRunLoop.h: 110 - Made Task::performTask() private and make Task befriend the WorkerRunLoop class. 111 This ensures that only the WorkerRunLoop may call performTask(). 112 Note: this change only formalizes and hardens a relationship that was already 113 in place before this. 114 115 * workers/WorkerThread.cpp: 116 (WebCore::WorkerThread::start): 117 (WebCore::WorkerThread::workerThread): 118 (WebCore::WorkerThread::stop): 119 * workers/WorkerThread.h: 120 - Renamed m_threadCreationMutex to m_threadCreationAndWorkerGlobalScopeMutex so 121 that it more accurately describes what it guards. 122 1 123 2017-05-15 Myles C. Maxfield <mmaxfield@apple.com> 2 124 -
trunk/Source/WebCore/bindings/js/JSDOMPromiseDeferred.cpp
r216826 r216876 1 1 /* 2 * Copyright (C) 2013 , 2016Apple Inc. All rights reserved.2 * Copyright (C) 2013-2017 Apple Inc. All rights reserved. 3 3 * 4 4 * Redistribution and use in source and binary forms, with or without … … 50 50 return; 51 51 52 VM& vm = exec.vm(); 53 auto scope = DECLARE_THROW_SCOPE(vm); 54 52 55 CallData callData; 53 56 CallType callType = getCallData(function, callData); … … 58 61 59 62 call(&exec, function, callType, callData, jsUndefined(), arguments); 63 64 // DeferredPromise should only be used by internal implementations that are well behaved. 65 // In practice, the only exception we should ever see here is the TerminatedExecutionException. 66 ASSERT_UNUSED(scope, !scope.exception() || isTerminatedExecutionException(vm, scope.exception())); 60 67 61 68 clear(); -
trunk/Source/WebCore/bindings/js/WorkerScriptController.cpp
r216826 r216876 153 153 void WorkerScriptController::scheduleExecutionTermination() 154 154 { 155 if (m_isTerminatingExecution) 156 return; 157 155 158 { 156 159 // The mutex provides a memory barrier to ensure that once -
trunk/Source/WebCore/workers/WorkerRunLoop.cpp
r216826 r216876 1 1 /* 2 2 * Copyright (C) 2009 Google Inc. All rights reserved. 3 * Copyright (C) 2016 Apple Inc. All rights reserved.3 * Copyright (C) 2016-2017 Apple Inc. All rights reserved. 4 4 * 5 5 * Redistribution and use in source and binary forms, with or without … … 200 200 201 201 case MessageQueueMessageReceived: 202 task->performTask( *this,context);202 task->performTask(context); 203 203 break; 204 204 … … 229 229 if (!task) 230 230 return; 231 task->performTask( *this,context);231 task->performTask(context); 232 232 } 233 233 } … … 253 253 } 254 254 255 void WorkerRunLoop::Task::performTask(const WorkerRunLoop& runLoop, WorkerGlobalScope* context) 256 { 257 if ((!context->isClosing() && !runLoop.terminated()) || m_task.isCleanupTask()) 255 void WorkerRunLoop::Task::performTask(WorkerGlobalScope* context) 256 { 257 ASSERT(context->script()); 258 if ((!context->isClosing() && !context->script()->isTerminatingExecution()) || m_task.isCleanupTask()) 258 259 m_task.performTask(*context); 259 260 } -
trunk/Source/WebCore/workers/WorkerRunLoop.h
r216826 r216876 1 1 /* 2 2 * Copyright (C) 2009 Google Inc. All rights reserved. 3 * 3 * Copyright (C) 2017 Apple Inc. All rights reserved. 4 * 4 5 * Redistribution and use in source and binary forms, with or without 5 6 * modification, are permitted provided that the following conditions are … … 71 72 Task(ScriptExecutionContext::Task&&, const String& mode); 72 73 const String& mode() const { return m_mode; } 73 void performTask(const WorkerRunLoop&, WorkerGlobalScope*);74 74 75 75 private: 76 void performTask(WorkerGlobalScope*); 77 76 78 ScriptExecutionContext::Task m_task; 77 79 String m_mode; 80 81 friend class WorkerRunLoop; 78 82 }; 79 83 -
trunk/Source/WebCore/workers/WorkerThread.cpp
r216826 r216876 1 1 /* 2 * Copyright (C) 2008 Apple Inc. All Rights Reserved.2 * Copyright (C) 2008-2017 Apple Inc. All Rights Reserved. 3 3 * 4 4 * Redistribution and use in source and binary forms, with or without … … 134 134 { 135 135 // Mutex protection is necessary to ensure that m_thread is initialized when the thread starts. 136 LockHolder lock(m_threadCreation Mutex);136 LockHolder lock(m_threadCreationAndWorkerGlobalScopeMutex); 137 137 138 138 if (m_thread) … … 161 161 #endif 162 162 163 WorkerScriptController* scriptController; 163 164 { 164 LockHolder lock(m_threadCreationMutex); 165 // Mutex protection is necessary to ensure that we don't change m_workerGlobalScope 166 // while WorkerThread::stop() is accessing it. Note that WorkerThread::stop() can 167 // be called before we've finished creating the WorkerGlobalScope. 168 LockHolder lock(m_threadCreationAndWorkerGlobalScopeMutex); 165 169 m_workerGlobalScope = createWorkerGlobalScope(m_startupData->m_scriptURL, m_startupData->m_identifier, m_startupData->m_userAgent, m_startupData->m_contentSecurityPolicyResponseHeaders, m_startupData->m_shouldBypassMainWorldContentSecurityPolicy, WTFMove(m_startupData->m_topOrigin), m_startupData->m_timeOrigin); 170 171 scriptController = m_workerGlobalScope->script(); 166 172 167 173 if (m_runLoop.terminated()) { 168 174 // The worker was terminated before the thread had a chance to run. Since the context didn't exist yet, 169 175 // forbidExecution() couldn't be called from stop(). 170 m_workerGlobalScope->script()->forbidExecution(); 176 scriptController->scheduleExecutionTermination(); 177 scriptController->forbidExecution(); 171 178 } 172 179 } … … 177 184 // If the worker was somehow terminated while processing debugger commands. 178 185 if (m_runLoop.terminated()) 179 m_workerGlobalScope->script()->forbidExecution(); 180 } 181 182 WorkerScriptController* script = m_workerGlobalScope->script(); 183 script->evaluate(ScriptSourceCode(m_startupData->m_sourceCode, m_startupData->m_scriptURL)); 186 scriptController->forbidExecution(); 187 } 188 189 scriptController->evaluate(ScriptSourceCode(m_startupData->m_sourceCode, m_startupData->m_scriptURL)); 184 190 // Free the startup data to cause its member variable deref's happen on the worker's thread (since 185 191 // all ref/derefs of these objects are happening on the thread at this point). Note that … … 197 203 ASSERT(m_workerGlobalScope->hasOneRef()); 198 204 205 RefPtr<WorkerGlobalScope> workerGlobalScopeToDelete; 206 { 207 // Mutex protection is necessary to ensure that we don't change m_workerGlobalScope 208 // while WorkerThread::stop is accessing it. 209 LockHolder lock(m_threadCreationAndWorkerGlobalScopeMutex); 210 211 // Delay the destruction of the WorkerGlobalScope context until after we've unlocked the 212 // m_threadCreationAndWorkerGlobalScopeMutex. This is needed because destructing the 213 // context will trigger the main thread to race against us to delete the WorkerThread 214 // object, and the WorkerThread object owns the mutex we need to unlock after this. 215 workerGlobalScopeToDelete = WTFMove(m_workerGlobalScope); 216 } 217 199 218 // The below assignment will destroy the context, which will in turn notify messaging proxy. 200 219 // We cannot let any objects survive past thread exit, because no other thread will run GC or otherwise destroy them. 201 m_workerGlobalScope = nullptr;220 workerGlobalScopeToDelete = nullptr; 202 221 203 222 // Clean up WebCore::ThreadGlobalData before WTF::WTFThreadData goes away! … … 232 251 void WorkerThread::stop() 233 252 { 234 // Mutex protection is necessary because stop() can be called before the context is fully created. 235 LockHolder lock(m_threadCreationMutex); 253 // Mutex protection is necessary to ensure that m_workerGlobalScope isn't changed by 254 // WorkerThread::workerThread() while we're accessing it. Note also that stop() can 255 // be called before m_workerGlobalScope is fully created. 256 LockHolder lock(m_threadCreationAndWorkerGlobalScopeMutex); 236 257 237 258 // Ensure that tasks are being handled by thread event loop. If script execution weren't forbidden, a while(1) loop in JS could keep the thread alive forever. -
trunk/Source/WebCore/workers/WorkerThread.h
r216826 r216876 1 1 /* 2 * Copyright (C) 2008 , 2016Apple Inc. All Rights Reserved.2 * Copyright (C) 2008-2017 Apple Inc. All Rights Reserved. 3 3 * 4 4 * Redistribution and use in source and binary forms, with or without … … 107 107 108 108 RefPtr<WorkerGlobalScope> m_workerGlobalScope; 109 Lock m_threadCreation Mutex;109 Lock m_threadCreationAndWorkerGlobalScopeMutex; 110 110 111 111 std::unique_ptr<WorkerThreadStartupData> m_startupData;
Note:
See TracChangeset
for help on using the changeset viewer.