Closed Bug 1748602 Opened 2 years ago Closed 2 years ago

Wasm heap-use-after-free: Instant tab crash in [@ js::gc::StoreBuffer::MonoTypeBuffer<T>::trace]

Categories

(Core :: JavaScript: WebAssembly, defect, P1)

defect

Tracking

()

VERIFIED FIXED
97 Branch
Tracking Status
firefox-esr91 --- unaffected
firefox95 --- unaffected
firefox96 --- unaffected
firefox97 --- verified

People

(Reporter: jan, Assigned: rhunt)

References

(Blocks 1 open bug, Regression)

Details

(5 keywords, Whiteboard: [sec-survey])

Crash Data

Attachments

(2 files)

Debian Testing, Gnome Xwayland, Intel
I get an instant tab crash when opening my local wasm project.

mozregression --good 2021-12-01 --bad 20220104214425 -a https://terrax.localhost:8443/de/media/tv/phoenix

10:54.66 INFO: Last good revision: 58a45475b5249cce6f9be93ba2824e24819cb3f5
10:54.66 INFO: First bad revision: 67ae30727ed835cf73153a4ee8f0c9c7931aab09
10:54.66 INFO: Pushlog:
https://hg.mozilla.org/integration/autoland/pushloghtml?fromchange=58a45475b5249cce6f9be93ba2824e24819cb3f5&tochange=67ae30727ed835cf73153a4ee8f0c9c7931aab09

67ae30727ed835cf73153a4ee8f0c9c7931aab09 Ryan Hunt — Bug 1642412 - wasm: Remove unnecessary code from Instance for inline table methods. r=lth
90fa382b5a203d2e28bfa69f29ab8edee150b77b Ryan Hunt — Bug 1642412 - wasm: Optimize ion table.get/set/size. r=lth
40f861f62a0f58040cb6cadf073bc5e97a44231d Ryan Hunt — Bug 1642412 - wasm: Optimize baseline table.size/get/set. r=lth

Reproduced with a fresh profile:

Maybe Fission related. (DOMFissionEnabled=1)

Crash report: https://crash-stats.mozilla.org/report/index/433703d5-110d-4560-bb8c-b5b9d0220105

Reason: SIGSEGV / SI_KERNEL

Top 8 frames of crashing thread:

0 libxul.so js::gc::StoreBuffer::MonoTypeBuffer<js::gc::StoreBuffer::CellPtrEdge<JSObject> >::trace js/src/gc/Tenuring.cpp:161
1 libxul.so js::Nursery::collect js/src/gc/Nursery.cpp:1108
2 libxul.so js::gc::GCRuntime::collectNursery js/src/gc/GC.cpp:4092
3 libxul.so js::gc::GCRuntime::minorGC js/src/gc/GC.cpp:4058
4 libxul.so js::NativeObject::create js/src/vm/NativeObject-inl.h:445
5 libxul.so js::NewPlainObjectOptimizedFallback js/src/vm/Interpreter.cpp:5319
6 None @0x00000c7dc117aa91 
7 libxul.so _fini 

Asan Nightly:

=================================================================
==27714==ERROR: AddressSanitizer: heap-use-after-free on address 0x61d0000fe138 at pc 0x7f84c95c9a3f bp 0x7ffc2515deb0 sp 0x7ffc2515dea8
READ of size 8 at 0x61d0000fe138 thread T0 (WebCOOP+COEP Co)
    #0 0x7f84c95c9a3e in trace /builds/worker/checkouts/gecko/js/src/gc/Tenuring.cpp:374:14
    #1 0x7f84c95c9a3e in js::gc::StoreBuffer::MonoTypeBuffer<js::gc::StoreBuffer::CellPtrEdge<JSObject> >::trace(js::TenuringTracer&) /builds/worker/checkouts/gecko/js/src/gc/Tenuring.cpp:161:15
    #2 0x7f84c9567030 in traceCells /builds/worker/checkouts/gecko/js/src/gc/StoreBuffer.h:536:16
    #3 0x7f84c9567030 in js::Nursery::traceRoots(js::gc::AutoGCSession&, js::TenuringTracer&) /builds/worker/checkouts/gecko/js/src/gc/Nursery.cpp:1315:6
    #4 0x7f84c95642d6 in js::Nursery::doCollection(JS::GCReason) /builds/worker/checkouts/gecko/js/src/gc/Nursery.cpp:1228:3
    #5 0x7f84c95632ad in js::Nursery::collect(JS::GCOptions, JS::GCReason) /builds/worker/checkouts/gecko/js/src/gc/Nursery.cpp:1108:31
    #6 0x7f84c9514f06 in js::gc::GCRuntime::collectNursery(JS::GCOptions, JS::GCReason, js::gcstats::PhaseKind) /builds/worker/checkouts/gecko/js/src/gc/GC.cpp:4092:13
    #7 0x7f84c951bfa9 in js::gc::GCRuntime::minorGC(JS::GCReason, js::gcstats::PhaseKind) /builds/worker/checkouts/gecko/js/src/gc/GC.cpp:4058:3
    #8 0x7f84c94d7685 in tryNewNurseryObject<js::CanGC> /builds/worker/checkouts/gecko/js/src/gc/Allocator.cpp:128:23
    #9 0x7f84c94d7685 in JSObject* js::AllocateObject<(js::AllowGC)1>(JSContext*, js::gc::AllocKind, unsigned long, js::gc::InitialHeap, JSClass const*, js::gc::AllocSite*) /builds/worker/checkouts/gecko/js/src/gc/Allocator.cpp:79:28
    #10 0x7f84c85ec099 in create /builds/worker/checkouts/gecko/js/src/vm/ArrayObject-inl.h:43:7
    #11 0x7f84c85ec099 in NewArrayWithShape<0U> /builds/worker/checkouts/gecko/js/src/builtin/Array.cpp:4002:22
    #12 0x7f84c85ec099 in NewArray<0U> /builds/worker/checkouts/gecko/js/src/builtin/Array.cpp:4071:10
    #13 0x7f84c85ec099 in js::NewDenseEmptyArray(JSContext*) /builds/worker/checkouts/gecko/js/src/builtin/Array.cpp:4174:10
    #14 0x7f84c9036df4 in RememberSourceURL /builds/worker/checkouts/gecko/js/src/debugger/Debugger.cpp:2419:14
    #15 0x7f84c9036df4 in js::DebugAPI::onNewScript(JSContext*, JS::Handle<JSScript*>) /builds/worker/checkouts/gecko/js/src/debugger/Debugger.cpp:2445:12
    #16 0x7f84c932a2ee in FireOnNewScript /builds/worker/checkouts/gecko/js/src/frontend/BytecodeCompiler.cpp:1389:5
    #17 0x7f84c932a2ee in js::frontend::InstantiateStencils(JSContext*, js::frontend::CompilationInput&, js::frontend::CompilationStencil const&, js::frontend::CompilationGCOutput&) /builds/worker/checkouts/gecko/js/src/frontend/BytecodeCompiler.cpp:395:5
    #18 0x7f84c944185c in JS::InstantiateGlobalStencil(JSContext*, JS::InstantiateOptions const&, js::frontend::CompilationStencil*) /builds/worker/checkouts/gecko/js/src/frontend/Stencil.cpp:4403:8
    #19 0x7f84bc8e60ce in EvalStencil /builds/worker/checkouts/gecko/js/xpconnect/loader/mozJSSubScriptLoader.cpp:134:27
    #20 0x7f84bc8e60ce in mozJSSubScriptLoader::DoLoadSubScriptWithOptions(nsTSubstring<char16_t> const&, LoadSubScriptOptions&, JSContext*, JS::MutableHandle<JS::Value>) /builds/worker/checkouts/gecko/js/xpconnect/loader/mozJSSubScriptLoader.cpp:482:13
    #21 0x7f84bc8e445c in mozJSSubScriptLoader::LoadSubScript(nsTSubstring<char16_t> const&, JS::Handle<JS::Value>, JSContext*, JS::MutableHandle<JS::Value>) /builds/worker/checkouts/gecko/js/xpconnect/loader/mozJSSubScriptLoader.cpp:308:10
    #22 0x7f84ba940bc5 in NS_InvokeByIndex /builds/worker/checkouts/gecko/xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_x86_64_unix.S:101
    #23 0x7f84bc9c7fd2 in Invoke /builds/worker/checkouts/gecko/js/xpconnect/src/XPCWrappedNative.cpp:1631:10
    #24 0x7f84bc9c7fd2 in Call /builds/worker/checkouts/gecko/js/xpconnect/src/XPCWrappedNative.cpp:1184:19
    #25 0x7f84bc9c7fd2 in XPCWrappedNative::CallMethod(XPCCallContext&, XPCWrappedNative::CallMode) /builds/worker/checkouts/gecko/js/xpconnect/src/XPCWrappedNative.cpp:1130:23
    #26 0x7f84bc9cde23 in XPC_WN_CallMethod(JSContext*, unsigned int, JS::Value*) /builds/worker/checkouts/gecko/js/xpconnect/src/XPCWrappedNativeJSOps.cpp:923:10
    #27 0x7f84c85860ef in CallJSNative /builds/worker/checkouts/gecko/js/src/vm/Interpreter.cpp:425:13
    #28 0x7f84c85860ef in js::InternalCallOrConstruct(JSContext*, JS::CallArgs const&, js::MaybeConstruct, js::CallReason) /builds/worker/checkouts/gecko/js/src/vm/Interpreter.cpp:512:12
    #29 0x7f84c85718f4 in CallFromStack /builds/worker/checkouts/gecko/js/src/vm/Interpreter.cpp:576:10
    #30 0x7f84c85718f4 in Interpret(JSContext*, js::RunState&) /builds/worker/checkouts/gecko/js/src/vm/Interpreter.cpp:3309:16
    #31 0x7f84c855bec8 in js::RunScript(JSContext*, js::RunState&) /builds/worker/checkouts/gecko/js/src/vm/Interpreter.cpp:394:13
    #32 0x7f84c8586350 in js::InternalCallOrConstruct(JSContext*, JS::CallArgs const&, js::MaybeConstruct, js::CallReason) /builds/worker/checkouts/gecko/js/src/vm/Interpreter.cpp:544:13
    #33 0x7f84c8588aa5 in js::Call(JSContext*, JS::Handle<JS::Value>, JS::Handle<JS::Value>, js::AnyInvokeArgs const&, JS::MutableHandle<JS::Value>, js::CallReason) /builds/worker/checkouts/gecko/js/src/vm/Interpreter.cpp:589:8
    #34 0x7f84c882db60 in JS::Call(JSContext*, JS::Handle<JS::Value>, JS::Handle<JS::Value>, JS::HandleValueArray const&, JS::MutableHandle<JS::Value>) /builds/worker/checkouts/gecko/js/src/vm/CallAndConstruct.cpp:117:10
    #35 0x7f84bffdd8d2 in mozilla::dom::EventListener::HandleEvent(mozilla::dom::BindingCallContext&, JS::Handle<JS::Value>, mozilla::dom::Event&, mozilla::ErrorResult&) /builds/worker/workspace/obj-build/dom/bindings/EventListenerBinding.cpp:62:8
    #36 0x7f84c2eafda7 in HandleEvent /builds/worker/workspace/obj-build/dist/include/mozilla/dom/EventListenerBinding.h:80:12
    #37 0x7f84c2eafda7 in HandleEvent /builds/worker/workspace/obj-build/dist/include/mozilla/dom/EventListenerBinding.h:93:12
    #38 0x7f84c2eafda7 in mozilla::dom::JSWindowActorProtocol::HandleEvent(mozilla::dom::Event*) /builds/worker/checkouts/gecko/dom/ipc/jsactor/JSWindowActorProtocol.cpp:228:18
    #39 0x7f84c0f729c6 in mozilla::EventListenerManager::HandleEventSubType(mozilla::EventListenerManager::Listener*, mozilla::dom::Event*, mozilla::dom::EventTarget*) /builds/worker/checkouts/gecko/dom/events/EventListenerManager.cpp:1309:22
    #40 0x7f84c0f749dd in mozilla::EventListenerManager::HandleEventInternal(nsPresContext*, mozilla::WidgetEvent*, mozilla::dom::Event**, mozilla::dom::EventTarget*, nsEventStatus*, bool) /builds/worker/checkouts/gecko/dom/events/EventListenerManager.cpp:1500:17
    #41 0x7f84c0f5dbed in HandleEvent /builds/worker/checkouts/gecko/dom/events/EventListenerManager.h:395:5
    #42 0x7f84c0f5dbed in mozilla::EventTargetChainItem::HandleEvent(mozilla::EventChainPostVisitor&, mozilla::ELMCreationDetector&) /builds/worker/checkouts/gecko/dom/events/EventDispatcher.cpp:348:17
    #43 0x7f84c0f5bad8 in mozilla::EventTargetChainItem::HandleEventTargetChain(nsTArray<mozilla::EventTargetChainItem>&, mozilla::EventChainPostVisitor&, mozilla::EventDispatchingCallback*, mozilla::ELMCreationDetector&) /builds/worker/checkouts/gecko/dom/events/EventDispatcher.cpp:550:16
    #44 0x7f84c0f6257a in mozilla::EventDispatcher::Dispatch(nsISupports*, nsPresContext*, mozilla::WidgetEvent*, mozilla::dom::Event*, nsEventStatus*, mozilla::EventDispatchingCallback*, nsTArray<mozilla::dom::EventTarget*>*) /builds/worker/checkouts/gecko/dom/events/EventDispatcher.cpp:1085:11
    #45 0x7f84c0f68639 in mozilla::EventDispatcher::DispatchDOMEvent(nsISupports*, mozilla::WidgetEvent*, mozilla::dom::Event*, nsPresContext*, nsEventStatus*) /builds/worker/checkouts/gecko/dom/events/EventDispatcher.cpp
    #46 0x7f84bddefbbd in nsWindowRoot::DispatchEvent(mozilla::dom::Event&, mozilla::dom::CallerType, mozilla::ErrorResult&) /builds/worker/checkouts/gecko/dom/base/nsWindowRoot.cpp:87:17
    #47 0x7f84bdb2bba0 in nsContentUtils::DispatchChromeEvent(mozilla::dom::Document*, nsISupports*, nsTSubstring<char16_t> const&, mozilla::CanBubble, mozilla::Cancelable, bool*) /builds/worker/checkouts/gecko/dom/base/nsContentUtils.cpp:4551:17
    #48 0x7f84bdfa1755 in operator() /builds/worker/checkouts/gecko/dom/base/Element.cpp:1307:9
    #49 0x7f84bdfa1755 in mozilla::detail::RunnableFunction<mozilla::dom::Element::NotifyUAWidgetSetupOrChange()::$_43>::Run() /builds/worker/workspace/obj-build/dist/include/nsThreadUtils.h:531:5
    #50 0x7f84bdb36ebd in nsContentUtils::RemoveScriptBlocker() /builds/worker/checkouts/gecko/dom/base/nsContentUtils.cpp:5687:17
    #51 0x7f84bde7af2e in mozilla::dom::Document::EndUpdate() /builds/worker/checkouts/gecko/dom/base/Document.cpp:7806:3
    #52 0x7f84be202afb in ~mozAutoDocUpdate /builds/worker/checkouts/gecko/dom/base/mozAutoDocUpdate.h:34:18
    #53 0x7f84be202afb in nsINode::ReplaceOrInsertBefore(bool, nsINode*, nsINode*, mozilla::ErrorResult&) /builds/worker/checkouts/gecko/dom/base/nsINode.cpp:2783:1
    #54 0x7f84beb4d8a1 in InsertBefore /builds/worker/checkouts/gecko/dom/base/nsINode.h:2031:12
    #55 0x7f84beb4d8a1 in mozilla::dom::Node_Binding::insertBefore(JSContext*, JS::Handle<JSObject*>, void*, JSJitMethodCallArgs const&) /builds/worker/workspace/obj-build/dom/bindings/NodeBinding.cpp:933:60
    #56 0x7f84c05846b2 in bool mozilla::dom::binding_detail::GenericMethod<mozilla::dom::binding_detail::NormalThisPolicy, mozilla::dom::binding_detail::ThrowExceptions>(JSContext*, unsigned int, JS::Value*) /builds/worker/checkouts/gecko/dom/bindings/BindingUtils.cpp:3306:13
    #57 0x386a745078ef  (<unknown module>)

0x61d0000fe138 is located 1208 bytes inside of 2048-byte region [0x61d0000fdc80,0x61d0000fe480)
freed by thread T0 (WebCOOP+COEP Co) here:
    #0 0x564dc82ab7d2 in free /builds/worker/fetches/llvm-project/llvm/projects/compiler-rt/lib/asan/asan_malloc_linux.cpp:111:3
    #1 0x7f84ca513794 in js_free /builds/worker/workspace/obj-build/dist/include/js/Utility.h:411:3
    #2 0x7f84ca513794 in free_<js::HeapPtr<JSObject *> > /builds/worker/workspace/obj-build/dist/include/js/AllocPolicy.h:83:5
    #3 0x7f84ca513794 in mozilla::detail::VectorImpl<js::HeapPtr<JSObject*>, 0ul, js::SystemAllocPolicy, false>::growTo(mozilla::Vector<js::HeapPtr<JSObject*>, 0ul, js::SystemAllocPolicy>&, unsigned long) /builds/worker/workspace/obj-build/dist/include/mozilla/Vector.h:133:8
    #4 0x7f84ca5089e0 in growBy /builds/worker/workspace/obj-build/dist/include/mozilla/Vector.h:1105:9
    #5 0x7f84ca5089e0 in resize /builds/worker/workspace/obj-build/dist/include/mozilla/Vector.h:1153:12
    #6 0x7f84ca5089e0 in JS::GCVector<js::HeapPtr<JSObject*>, 0ul, js::SystemAllocPolicy>::resize(unsigned long) /builds/worker/workspace/obj-build/dist/include/js/GCVector.h:80:60
    #7 0x7f84ca50b888 in js::wasm::Table::grow(unsigned int) /builds/worker/checkouts/gecko/js/src/wasm/WasmTable.cpp:414:21
    #8 0x7f84ca39484e in js::wasm::Instance::tableGrow(js::wasm::Instance*, void*, unsigned int, unsigned int) /builds/worker/checkouts/gecko/js/src/wasm/WasmInstance.cpp:1192:32
    #9 0x386a749582e7  (<unknown module>)
    #10 0x386a746aa5b1  (<unknown module>)
    #11 0x386a746e6b0c  (<unknown module>)
    #12 0x386a746ec91b  (<unknown module>)
    #13 0x386a746a9e54  (<unknown module>)
    #14 0x386a745d29bf  (<unknown module>)
    #15 0x386a7465c424  (<unknown module>)
    #16 0x386a745e825b  (<unknown module>)
    #17 0x386a7466f19a  (<unknown module>)
    #18 0x386a74707222  (<unknown module>)
    #19 0x386a746b3783  (<unknown module>)
    #20 0x386a7469fae9  (<unknown module>)
    #21 0x386a746db598  (<unknown module>)
    #22 0x386a746eedc9  (<unknown module>)
    #23 0x386a746eecfb  (<unknown module>)

previously allocated by thread T0 (WebCOOP+COEP Co) here:
    #0 0x564dc82aba3d in malloc /builds/worker/fetches/llvm-project/llvm/projects/compiler-rt/lib/asan/asan_malloc_linux.cpp:129:3
    #1 0x7f84ca513397 in js_arena_malloc /builds/worker/workspace/obj-build/dist/include/js/Utility.h:364:10
    #2 0x7f84ca513397 in js_pod_arena_malloc<js::HeapPtr<JSObject *> > /builds/worker/workspace/obj-build/dist/include/js/Utility.h:573:26
    #3 0x7f84ca513397 in maybe_pod_arena_malloc<js::HeapPtr<JSObject *> > /builds/worker/workspace/obj-build/dist/include/js/AllocPolicy.h:31:12
    #4 0x7f84ca513397 in pod_arena_malloc<js::HeapPtr<JSObject *> > /builds/worker/workspace/obj-build/dist/include/js/AllocPolicy.h:44:12
    #5 0x7f84ca513397 in pod_malloc<js::HeapPtr<JSObject *> > /builds/worker/workspace/obj-build/dist/include/js/AllocPolicy.h:70:12
    #6 0x7f84ca513397 in mozilla::detail::VectorImpl<js::HeapPtr<JSObject*>, 0ul, js::SystemAllocPolicy, false>::growTo(mozilla::Vector<js::HeapPtr<JSObject*>, 0ul, js::SystemAllocPolicy>&, unsigned long) /builds/worker/workspace/obj-build/dist/include/mozilla/Vector.h:123:29
    #7 0x7f84ca5089e0 in growBy /builds/worker/workspace/obj-build/dist/include/mozilla/Vector.h:1105:9
    #8 0x7f84ca5089e0 in resize /builds/worker/workspace/obj-build/dist/include/mozilla/Vector.h:1153:12
    #9 0x7f84ca5089e0 in JS::GCVector<js::HeapPtr<JSObject*>, 0ul, js::SystemAllocPolicy>::resize(unsigned long) /builds/worker/workspace/obj-build/dist/include/js/GCVector.h:80:60
    #10 0x7f84ca50b888 in js::wasm::Table::grow(unsigned int) /builds/worker/checkouts/gecko/js/src/wasm/WasmTable.cpp:414:21
    #11 0x7f84ca39484e in js::wasm::Instance::tableGrow(js::wasm::Instance*, void*, unsigned int, unsigned int) /builds/worker/checkouts/gecko/js/src/wasm/WasmInstance.cpp:1192:32
    #12 0x386a749582e7  (<unknown module>)
    #13 0x386a746aa5b1  (<unknown module>)
    #14 0x386a746e73e8  (<unknown module>)
    #15 0x386a746db465  (<unknown module>)
    #16 0x386a746eedc9  (<unknown module>)
    #17 0x386a746eecfb  (<unknown module>)
    #18 0x386a74951e5e  (<unknown module>)

SUMMARY: AddressSanitizer: heap-use-after-free /builds/worker/checkouts/gecko/js/src/gc/Tenuring.cpp:374:14 in trace
Shadow bytes around the buggy address:
  0x0c3a80017bd0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c3a80017be0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c3a80017bf0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c3a80017c00: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c3a80017c10: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
=>0x0c3a80017c20: fd fd fd fd fd fd fd[fd]fd fd fd fd fd fd fd fd
  0x0c3a80017c30: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c3a80017c40: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c3a80017c50: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c3a80017c60: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c3a80017c70: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
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
==27714==ABORTING
Flags: needinfo?(rhunt)

Will take a look at this. Marking as security sensitive due to UaF until I know more.

Group: core-security
Group: core-security → javascript-core-security

Have uploaded my experimental project here: https://mail.terrax.net:8443/de/media/tv/phoenix
The tab crash immediately occurs when opening the page.

It's a rust/wasm project with https://github.com/yewstack/yew/ and a rust/hyper/rustls server.

wasm-bindgen --reference-types --target no-modules --no-typescript --out-dir ../terrax_h2/htdocs/wasm/ --out-name app target/wasm32-unknown-unknown/release/terrax_wasm.wasm

The wasm file is https://mail.terrax.net:8443/wasm/app_bg.wasm.

We should expedite investigation in case we want to get a fix in before we branch on Monday.

Assignee: nobody → rhunt
Status: NEW → ASSIGNED
Priority: -- → P1

This appears to be an issue with our post barrier implementation. I believe it affects both wasm baseline and ion. The root issue is that our post barrier method will only ever add to the StoreBuffer, never remove from it [1].

The problematic sequence of events:

  1. Write a nursery object to a table element, which adds a store buffer entry for the table element
  2. Write a null or tenured object to that table element, which currently will not remove the store buffer entry
  3. Perform a moving grow (AKA resize) of the table
  4. Perform a GC
    The end result is a store buffer entry that points to the free'ed table memory from the grow. Our use of HeapPtr in the table elements is supposed to handle the moving grow by performing a write postbarrier of null in the destructor, which normally would remove the store buffer entry [2]. But the post write barrier implementation [2] will only remove the entry if the previous value was not null and in the nursery, assuming that the previous write that stored null or a tenured value would have removed the store buffer entry.

I will attach a reduced test case. This was not exposed by reftyped mutable globals because their storage doesn't move, unlike table entries. The wasm-GC feature may be affected, but it's behind a pref.

The proper solution is to extend our write post barriers to track the previous value and remove a store buffer entry when appropriate. I'm looking into how to do this, but the post barrier code in baseline and ion is tricky and this might have too much risk for a quick security patch.

Two stop gap solutions are available.

  1. Revert the regressing patches that added inline code for table.set
  2. Manually remove store buffer entries before a table resize [3]

I think (1) would be the quickest fix.

[1] https://searchfox.org/mozilla-central/rev/84c1031f19b2cf8221da0850983f8d0269f6efd1/js/src/wasm/WasmInstance.cpp#1341
[2] https://searchfox.org/mozilla-central/rev/84c1031f19b2cf8221da0850983f8d0269f6efd1/js/src/gc/StoreBuffer.h#654-656
[3]

      auto& buffer = cx->runtime()->gc.storeBuffer();
      for (uint32_t i = 0; i < length_; i++) {
        HeapPtrObject& valueAddr = objects_[i];
        // Conservatively remove a (potentially non-existent) store buffer entry before resizing
        if (!valueAddr.get() || !valueAddr.get()->storeBuffer()) {
          buffer.unputCell(valueAddr.unbarrieredAddress());
        }
      }
      if (!objects_.resize(newLength.value())) {
        return -1;
      }
Flags: needinfo?(rhunt)
Has Regression Range: --- → yes
Has STR: --- → yes
OS: Linux → All
Hardware: x86_64 → All
Group: javascript-core-security → core-security-release
Status: ASSIGNED → RESOLVED
Closed: 2 years ago
Resolution: --- → FIXED
Target Milestone: --- → 97 Branch

The crash no longer occurs with Nightly 20220108094024. Thank you.

I'm curious: Does this bug (asan info, regression range, unreduced testcase) qualify for https://www.mozilla.org/en-US/security/client-bug-bounty/?

Status: RESOLVED → VERIFIED
Flags: sec-bounty?
Flags: sec-bounty? → sec-bounty+

As part of a security bug pattern analysis, we are requesting your help with a high level analysis of this bug. It is our hope to develop static analysis (or potentially runtime/dynamic analysis) in the future to identify classes of bugs.

Please visit this google form to reply.

Flags: needinfo?(rhunt)
Whiteboard: [sec-survey]
Flags: needinfo?(rhunt)
Group: core-security-release
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: