Open Bug 1751939 Opened 1 year ago Updated 5 months ago

Hit MOZ_CRASH(Promise not thread-safe) at /xpcom/base/nsISupportsImpl.cpp:43


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





(Reporter: jkratzer, Unassigned)


(Blocks 1 open bug)


(Keywords: testcase-wanted)


(2 files, 1 obsolete file)

Found while fuzzing mozilla-try rev 8b146d6f10c9 (built with: --enable-address-sanitizer --enable-fuzzing).

Unfortunately, the testcase used to initially identify this issue does not reproduce. Hopefully the stack trace will shed some light on what's happening here.

Hit MOZ_CRASH(Promise not thread-safe) at /xpcom/base/nsISupportsImpl.cpp:43

    ==7221==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000001 (pc 0x7f1f332eb185 bp 0x7ffccfa0e310 sp 0x7ffccfa0e300 T0)
    ==7221==The signal is caused by a WRITE memory access.
    ==7221==Hint: address points to the zero page.
        #0 0x7f1f332eb185 in MOZ_Crash /builds/worker/workspace/obj-build/dist/include/mozilla/Assertions.h:261:3
        #1 0x7f1f332eb185 in nsAutoOwningThread::AssertCurrentThreadOwnsMe(char const*) const /gecko/xpcom/base/nsISupportsImpl.cpp:43:5
        #2 0x7f1f3328e9e9 in AssertOwnership<24> /builds/worker/workspace/obj-build/dist/include/nsISupportsImpl.h:60:5
        #3 0x7f1f3328e9e9 in mozilla::dom::Promise::Release() /builds/worker/workspace/obj-build/dist/include/mozilla/dom/Promise.h:53:3
        #4 0x7f1f36b859a2 in Release /builds/worker/workspace/obj-build/dist/include/mozilla/RefPtr.h:50:40
        #5 0x7f1f36b859a2 in Release /builds/worker/workspace/obj-build/dist/include/mozilla/RefPtr.h:381:36
        #6 0x7f1f36b859a2 in ~RefPtr /builds/worker/workspace/obj-build/dist/include/mozilla/RefPtr.h:81:7
        #7 0x7f1f36b859a2 in mozilla::dom::BodyConsumer::~BodyConsumer() /gecko/dom/base/BodyConsumer.cpp:381:29
        #8 0x7f1f36b84e05 in Release /gecko/dom/base/BodyConsumer.cpp:820:1
        #9 0x7f1f36b84e05 in Release /builds/worker/workspace/obj-build/dist/include/mozilla/RefPtr.h:50:40
        #10 0x7f1f36b84e05 in Release /builds/worker/workspace/obj-build/dist/include/mozilla/RefPtr.h:381:36
        #11 0x7f1f36b84e05 in RefPtr<mozilla::dom::BodyConsumer>::~RefPtr() /builds/worker/workspace/obj-build/dist/include/mozilla/RefPtr.h:81:7
        #12 0x7f1f36bab775 in ~ConsumeBodyDoneObserver /gecko/dom/base/BodyConsumer.cpp:263:38
        #13 0x7f1f36bab775 in mozilla::dom::(anonymous namespace)::ConsumeBodyDoneObserver::Release() /gecko/dom/base/BodyConsumer.cpp:269:1
        #14 0x7f1f338d061e in assign_assuming_AddRef /builds/worker/workspace/obj-build/dist/include/nsCOMPtr.h:377:7
        #15 0x7f1f338d061e in assign_assuming_AddRef /builds/worker/workspace/obj-build/dist/include/nsCOMPtr.h:400:20
        #16 0x7f1f338d061e in operator= /builds/worker/workspace/obj-build/dist/include/nsCOMPtr.h:696:5
        #17 0x7f1f338d061e in mozilla::net::nsStreamLoader::OnStopRequest(nsIRequest*, nsresult) /gecko/netwerk/base/nsStreamLoader.cpp:98:15
        #18 0x7f1f3380d82b in nsInputStreamPump::OnStateStop() /gecko/netwerk/base/nsInputStreamPump.cpp:636:16
        #19 0x7f1f3380bd7e in nsInputStreamPump::OnInputStreamReady(nsIAsyncInputStream*) /gecko/netwerk/base/nsInputStreamPump.cpp:381:21
        #20 0x7f1f3339d1bd in mozilla::NonBlockingAsyncInputStream::RunAsyncWaitCallback(mozilla::NonBlockingAsyncInputStream::AsyncWaitRunnable*, already_AddRefed<nsIInputStreamCallback>) /gecko/xpcom/io/NonBlockingAsyncInputStream.cpp:398:13
        #21 0x7f1f3339bb2e in mozilla::NonBlockingAsyncInputStream::AsyncWaitRunnable::Run() /gecko/xpcom/io/NonBlockingAsyncInputStream.cpp:33:14
        #22 0x7f1f334ca422 in mozilla::ThrottledEventQueue::Inner::ExecuteRunnable() /gecko/xpcom/threads/ThrottledEventQueue.cpp:254:22
        #23 0x7f1f334c23ff in mozilla::ThrottledEventQueue::Inner::Executor::Run() /gecko/xpcom/threads/ThrottledEventQueue.cpp:81:15
        #24 0x7f1f334c4292 in mozilla::RunnableTask::Run() /gecko/xpcom/threads/TaskController.cpp:468:16
        #25 0x7f1f33489b8d in mozilla::TaskController::DoExecuteNextTaskOnlyMainThreadInternal(mozilla::detail::BaseAutoLock<mozilla::Mutex&> const&) /gecko/xpcom/threads/TaskController.cpp:771:26
        #26 0x7f1f334870e8 in mozilla::TaskController::ExecuteNextTaskOnlyMainThreadInternal(mozilla::detail::BaseAutoLock<mozilla::Mutex&> const&) /gecko/xpcom/threads/TaskController.cpp:607:15
        #27 0x7f1f334877f9 in mozilla::TaskController::ProcessPendingMTTask(bool) /gecko/xpcom/threads/TaskController.cpp:391:36
        #28 0x7f1f334cc691 in operator() /gecko/xpcom/threads/TaskController.cpp:124:37
        #29 0x7f1f334cc691 in mozilla::detail::RunnableFunction<mozilla::TaskController::InitializeInternal()::$_0>::Run() /gecko/xpcom/threads/nsThreadUtils.h:531:5
        #30 0x7f1f334a9e87 in nsThread::ProcessNextEvent(bool, bool*) /gecko/xpcom/threads/nsThread.cpp:1195:16
        #31 0x7f1f334b506c in NS_ProcessNextEvent(nsIThread*, bool) /gecko/xpcom/threads/nsThreadUtils.cpp:467:10
        #32 0x7f1f349cbc7f in mozilla::ipc::MessagePump::Run(base::MessagePump::Delegate*) /gecko/ipc/glue/MessagePump.cpp:85:21
        #33 0x7f1f3484b9a1 in RunInternal /gecko/ipc/chromium/src/base/
        #34 0x7f1f3484b9a1 in RunHandler /gecko/ipc/chromium/src/base/
        #35 0x7f1f3484b9a1 in MessageLoop::Run() /gecko/ipc/chromium/src/base/
        #36 0x7f1f3b8189e7 in nsBaseAppShell::Run() /gecko/widget/nsBaseAppShell.cpp:137:27
        #37 0x7f1f40546d7f in XRE_RunAppShell() /gecko/toolkit/xre/nsEmbedFunctions.cpp:870:20
        #38 0x7f1f3484b9a1 in RunInternal /gecko/ipc/chromium/src/base/
        #39 0x7f1f3484b9a1 in RunHandler /gecko/ipc/chromium/src/base/
        #40 0x7f1f3484b9a1 in MessageLoop::Run() /gecko/ipc/chromium/src/base/
        #41 0x7f1f40545fb3 in XRE_InitChildProcess(int, char**, XREChildData const*) /gecko/toolkit/xre/nsEmbedFunctions.cpp:707:34
        #42 0x55d343a3bf9d in content_process_main(mozilla::Bootstrap*, int, char**) /gecko/browser/app/../../ipc/contentproc/plugin-container.cpp:57:28
        #43 0x55d343a3c3c8 in main /gecko/browser/app/nsBrowserApp.cpp:327:18
        #44 0x7f1f57be80b2 in __libc_start_main /build/glibc-eX1tMB/glibc-2.31/csu/../csu/libc-start.c:308:16
        #45 0x55d34398b069 in _start (/home/worker/builds/try-20220117185637-fuzzing-asan-opt/firefox+0x5d069)
    AddressSanitizer can not provide additional info.
    SUMMARY: AddressSanitizer: SEGV /builds/worker/workspace/obj-build/dist/include/mozilla/Assertions.h:261:3 in MOZ_Crash
Attached file Detailed Crash Information (obsolete) —

Given that this was a DOM Streams build, I'm marking it as a ship-blocker for DOM Streams until we know more.

Blocks: 1735656

Jason: Despite the lack of reproduction, could you post the test case? Below I'm trying to do some reasoning out of what's going on, and seeing the test case would help prove or disprove my suspision. I suspect this isn't a failure limited to DOM Streams.

Analysis By Stacktrace

So we crash while destroying a promise held by BodyConsumer.

There is only one promise held by BodyConsumer, so we have to be destroying BodyConsumer::mConsumePromise.

Inspecting how mConsumePromise is used, it is only ever set in the constructor, nulled out, or std::moveed into a local. So the promise must be the one provided to the constructor.

There's only one call to the BodyConsumer constructor, inside of BodyConsumer::Create -- and this function also creates the Promise. So we know that BodyConsumer and the mConsumePromise must have been created on the same thread; and since the observed crash is happening while running on main thread (unless DoExecuteNextTaskOnlyMainThreadInternal is lying to me) we know that BodyConsumer::Create must have been created off the main thread.

The only caller to BodyConsumer::Create comes from Blob::ConsumeBody -- this is used to power Blob::Text and Blob::ArrayBuffer, both of which are defined by the Blob WebIDL

So if I had to guess, this failure has to do with a blob being created on a worker. Then, there's something to do with an nsInputStreamPump calling the listener's OnStopRequest on the main thread, which frees the observer, which in turn frees the BodyConsumer.

This is likely tricky to reproduce because the promise has to be collectable at this point, which can only happen if the Promise wrapper has already been collected, so you need to have a GC having occurred, then have the nsInputStreamPump call OnStopRequest.

Of course, I understand nothing of how to fix this; but I am very skeptical this is a DOM streams problem directly.

Going to move this to DOM Workers as a guess... not 100% sure though.

No longer blocks: 1735656
Component: DOM: Streams → DOM: Workers
Flags: needinfo?(jkratzer)

Testcase found while fuzzing mozilla-try rev 8b146d6f10c9 (built with: --enable-address-sanitizer --enable-fuzzing).

As the testcase is not reproducible, it is has not been minimized. Please let me know if you have any questions.

Attached file Testcase
Attachment #9260637 - Attachment is obsolete: true
Flags: needinfo?(jkratzer)
Attachment #9260853 - Attachment description: Testcase for comment 2 → Testcase

Oh wow; you're not wrong about an un-reduced case; neverthless, I do spot some use of workers and some use of Blob.prototype.arrayBuffer in the worker...

Looking at BodyConsumer::ContinueConsumeBlobBody I wonder if we miss a ReleaseObject(); in that blob failure case? (Even if so it might not be the reason for this, of course, I am just looking at the code.)

Severity: -- → S3
Priority: -- → P3
You need to log in before you can comment on or make changes to this bug.