Closed Bug 1614101 Opened 5 years ago Closed 5 years ago

heap-use-after-free in [@ nsFrameSelection::cycleCollection::TraverseNative]

Categories

(Core :: Layout, defect, P1)

defect

Tracking

()

RESOLVED FIXED
mozilla75
Tracking Status
firefox-esr68 --- unaffected
firefox73 --- unaffected
firefox74 + fixed
firefox75 + fixed

People

(Reporter: tsmith, Assigned: TYLin)

References

(Blocks 1 open bug, Regression)

Details

(5 keywords)

Crash Data

Attachments

(4 files)

Attached file testcase.zip

This was hit while reducing bug 1613547. Maybe it's just a dupe.

Report from m-c 20200207-882200a11bcf

The prefs.js file in testcase.zip is required for to trigger this issue with the attached test case.

==88820==ERROR: AddressSanitizer: heap-use-after-free on address 0x622000073968 at pc 0x7fee16dba0f6 bp 0x7ffe65f60660 sp 0x7ffe65f60658
READ of size 8 at 0x622000073968 thread T0 (file:// Content)
    #0 0x7fee16dba0f5 in get src/obj-firefox/dist/include/mozilla/RefPtr.h:284:27
    #1 0x7fee16dba0f5 in operator mozilla::dom::Document * src/obj-firefox/dist/include/mozilla/RefPtr.h:297:12
    #2 0x7fee16dba0f5 in GetDocument src/obj-firefox/dist/include/mozilla/PresShell.h:292:42
    #3 0x7fee16dba0f5 in nsFrameSelection::cycleCollection::TraverseNative(void*, nsCycleCollectionTraversalCallback&) src/layout/generic/nsFrameSelection.cpp:328:43
    #4 0x7fee0e205b38 in nsCycleCollectionParticipant::TraverseNativeAndJS(void*, nsCycleCollectionTraversalCallback&) src/xpcom/base/nsCycleCollectionParticipant.h:128:19
    #5 0x7fee0e22b3a0 in MayHaveChild src/xpcom/base/nsCycleCollector.cpp:2318:8
    #6 0x7fee0e22b3a0 in RemoveSkippableVisitor::Visit(nsPurpleBuffer&, nsPurpleBufferEntry*) src/xpcom/base/nsCycleCollector.cpp:2567:36
    #7 0x7fee0e2097f6 in void nsPurpleBuffer::VisitEntries<RemoveSkippableVisitor>(RemoveSkippableVisitor&) src/xpcom/base/nsCycleCollector.cpp:942:23
    #8 0x7fee0e2092f2 in nsPurpleBuffer::RemoveSkippable(nsCycleCollector*, js::SliceBudget&, bool, bool, void (*)()) src/xpcom/base/nsCycleCollector.cpp:2589:3
    #9 0x7fee0e20b213 in nsCycleCollector::ForgetSkippable(js::SliceBudget&, bool, bool) src/xpcom/base/nsCycleCollector.cpp:2660:14
    #10 0x7fee0e213ccd in nsCycleCollector_forgetSkippable(js::SliceBudget&, bool, bool) src/xpcom/base/nsCycleCollector.cpp:3864:21
    #11 0x7fee125d5cd5 in FireForgetSkippable(unsigned int, bool, mozilla::TimeStamp) src/dom/base/nsJSEnvironment.cpp:1250:3
    #12 0x7fee125dc513 in CCRunnerFired(mozilla::TimeStamp) src/dom/base/nsJSEnvironment.cpp:1898:7
    #13 0x7fee0e37c7d2 in operator() /builds/worker/fetches/clang/bin/../lib/gcc/x86_64-unknown-linux-gnu/7.4.0/../../../../include/c++/7.4.0/bits/std_function.h:706:14
    #14 0x7fee0e37c7d2 in mozilla::IdleTaskRunner::Run() src/xpcom/threads/IdleTaskRunner.cpp:58:14
    #15 0x7fee0e3c0218 in nsThread::ProcessNextEvent(bool, bool*) src/xpcom/threads/nsThread.cpp:1220:14
    #16 0x7fee0e3cb02c in NS_ProcessNextEvent(nsIThread*, bool) src/xpcom/threads/nsThreadUtils.cpp:486:10
    #17 0x7fee0f61be8f in mozilla::ipc::MessagePump::Run(base::MessagePump::Delegate*) src/ipc/glue/MessagePump.cpp:87:21
    #18 0x7fee0f515637 in RunInternal src/ipc/chromium/src/base/message_loop.cc:315:10
    #19 0x7fee0f515637 in RunHandler src/ipc/chromium/src/base/message_loop.cc:308:3
    #20 0x7fee0f515637 in MessageLoop::Run() src/ipc/chromium/src/base/message_loop.cc:290:3
    #21 0x7fee165e97a8 in nsBaseAppShell::Run() src/widget/nsBaseAppShell.cpp:137:27
    #22 0x7fee1a0f8b76 in XRE_RunAppShell() src/toolkit/xre/nsEmbedFunctions.cpp:943:20
    #23 0x7fee0f515637 in RunInternal src/ipc/chromium/src/base/message_loop.cc:315:10
    #24 0x7fee0f515637 in RunHandler src/ipc/chromium/src/base/message_loop.cc:308:3
    #25 0x7fee0f515637 in MessageLoop::Run() src/ipc/chromium/src/base/message_loop.cc:290:3
    #26 0x7fee1a0f821f in XRE_InitChildProcess(int, char**, XREChildData const*) src/toolkit/xre/nsEmbedFunctions.cpp:778:34
    #27 0x563a39cfd403 in content_process_main src/browser/app/../../ipc/contentproc/plugin-container.cpp:56:28
    #28 0x563a39cfd403 in main src/browser/app/nsBrowserApp.cpp:303:18
    #29 0x7fee3155282f in __libc_start_main /build/glibc-LK5gWL/glibc-2.23/csu/../csu/libc-start.c:291
    #30 0x563a39c52ecc in _start (/home/user/workspace/browsers/m-c-20200207215354-fuzzing-asan-opt/firefox+0x9becc)

0x622000073968 is located 104 bytes inside of 5096-byte region [0x622000073900,0x622000074ce8)
freed by thread T0 (file:// Content) here:
    #0 0x563a39ccab9d in free /builds/worker/fetches/llvm-project/llvm/projects/compiler-rt/lib/asan/asan_malloc_linux.cc:123:3
    #1 0x7fee16aaf2b9 in operator delete src/obj-firefox/dist/include/mozilla/cxxalloc.h:51:10
    #2 0x7fee16aaf2b9 in mozilla::PresShell::Release() src/layout/base/PresShell.cpp:876:1
    #3 0x7fee125ec577 in Destruct src/obj-firefox/dist/include/nsTArray.h:548:45
    #4 0x7fee125ec577 in nsTArray_Impl<nsDelayedBlurOrFocusEvent, nsTArrayInfallibleAllocator>::DestructRange(unsigned long, unsigned long) src/obj-firefox/dist/include/nsTArray.h:2242:7
    #5 0x7fee125f4001 in RemoveElementsAtUnsafe src/obj-firefox/dist/include/nsTArray.h:2301:3
    #6 0x7fee125f4001 in nsTArray_Impl<nsDelayedBlurOrFocusEvent, nsTArrayInfallibleAllocator>::RemoveElementsAt(unsigned long, unsigned long) src/obj-firefox/dist/include/nsTArray.h:2295:3
    #7 0x7fee125714d0 in RemoveElementAt src/obj-firefox/dist/include/nsTArray.h:1757:45
    #8 0x7fee125714d0 in nsFocusManager::FireDelayedEvents(mozilla::dom::Document*) src/dom/base/nsFocusManager.cpp:990:33
    #9 0x7fee12315e8f in mozilla::dom::FireOrClearDelayedEvents(nsTArray<nsCOMPtr<mozilla::dom::Document> >&, bool) src/dom/base/Document.cpp:11267:11
    #10 0x7fee12315474 in mozilla::dom::Document::UnsuppressEventHandlingAndFireEvents(bool) src/dom/base/Document.cpp:11604:5
    #11 0x7fee120b8967 in nsGlobalWindowInner::FreeInnerObjects() src/dom/base/nsGlobalWindowInner.cpp:1110:13
    #12 0x7fee12111827 in nsGlobalWindowOuter::DetachFromDocShell(bool) src/dom/base/nsGlobalWindowOuter.cpp:2458:12
    #13 0x7fee1967e55c in nsDocShell::Destroy() src/docshell/base/nsDocShell.cpp:4559:20
    #14 0x7fee19c47cac in nsWebBrowser::SetDocShell(nsIDocShell*) src/toolkit/components/browser/nsWebBrowser.cpp:1193:23
    #15 0x7fee19c4623c in nsWebBrowser::InternalDestroy() src/toolkit/components/browser/nsWebBrowser.cpp:198:3
    #16 0x7fee19c4e43c in Destroy src/toolkit/components/browser/nsWebBrowser.cpp:904:3
    #17 0x7fee19c4e43c in non-virtual thunk to nsWebBrowser::Destroy() src/toolkit/components/browser/nsWebBrowser.cpp
    #18 0x7fee15d2ea5e in mozilla::dom::BrowserChild::DestroyWindow() src/dom/ipc/BrowserChild.cpp:978:31
    #19 0x7fee15d4d087 in mozilla::dom::BrowserChild::RecvDestroy() src/dom/ipc/BrowserChild.cpp:2472:3
    #20 0x7fee105de960 in mozilla::dom::PBrowserChild::OnMessageReceived(IPC::Message const&) src/obj-firefox/ipc/ipdl/PBrowserChild.cpp:6273:56
    #21 0x7fee0f9e1027 in mozilla::dom::PContentChild::OnMessageReceived(IPC::Message const&) src/obj-firefox/ipc/ipdl/PContentChild.cpp:8325:32
    #22 0x7fee0f60ffe2 in mozilla::ipc::MessageChannel::DispatchAsyncMessage(mozilla::ipc::ActorLifecycleProxy*, IPC::Message const&) src/ipc/glue/MessageChannel.cpp:2215:25
    #23 0x7fee0f60ac44 in mozilla::ipc::MessageChannel::DispatchMessage(IPC::Message&&) src/ipc/glue/MessageChannel.cpp:2137:9
    #24 0x7fee0f60cf0f in mozilla::ipc::MessageChannel::RunMessage(mozilla::ipc::MessageChannel::MessageTask&) src/ipc/glue/MessageChannel.cpp:1976:3

previously allocated by thread T0 (file:// Content) here:
    #0 0x563a39ccae1d in malloc /builds/worker/fetches/llvm-project/llvm/projects/compiler-rt/lib/asan/asan_malloc_linux.cc:145:3
    #1 0x563a39d0062d in moz_xmalloc src/memory/mozalloc/mozalloc.cpp:52:15
    #2 0x7fee122d9687 in operator new src/obj-firefox/dist/include/mozilla/cxxalloc.h:33:10
    #3 0x7fee122d9687 in mozilla::dom::Document::CreatePresShell(nsPresContext*, nsViewManager*) src/dom/base/Document.cpp:6154:33
    #4 0x7fee16b87ca0 in nsDocumentViewer::InitPresentationStuff(bool) src/layout/base/nsDocumentViewer.cpp:776:27
    #5 0x7fee16b876e2 in nsDocumentViewer::InitInternal(nsIWidget*, nsISupports*, mozilla::dom::WindowGlobalChild*, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&, bool, bool, bool) src/layout/base/nsDocumentViewer.cpp:969:10
    #6 0x7fee16b86a4a in nsDocumentViewer::Init(nsIWidget*, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&, mozilla::dom::WindowGlobalChild*) src/layout/base/nsDocumentViewer.cpp:758:10
    #7 0x7fee196be799 in nsDocShell::SetupNewViewer(nsIContentViewer*, mozilla::dom::WindowGlobalChild*) src/docshell/base/nsDocShell.cpp:8004:7
    #8 0x7fee196bd4f5 in nsDocShell::Embed(nsIContentViewer*, mozilla::dom::WindowGlobalChild*) src/docshell/base/nsDocShell.cpp:5777:17
    #9 0x7fee19670449 in nsDocShell::CreateContentViewer(nsTSubstring<char> const&, nsIRequest*, nsIStreamListener**) src/docshell/base/nsDocShell.cpp:7806:3
    #10 0x7fee1966d65c in nsDSURIContentListener::DoContent(nsTSubstring<char> const&, bool, nsIRequest*, nsIStreamListener**, bool*) src/docshell/base/nsDSURIContentListener.cpp:168:20
    #11 0x7fee10d9ed8a in nsDocumentOpenInfo::TryContentListener(nsIURIContentListener*, nsIChannel*) src/uriloader/base/nsURILoader.cpp:632:18
    #12 0x7fee10d9c35a in nsDocumentOpenInfo::DispatchContent(nsIRequest*, nsISupports*) src/uriloader/base/nsURILoader.cpp:313:9
    #13 0x7fee10d9ad21 in nsDocumentOpenInfo::OnStartRequest(nsIRequest*) src/uriloader/base/nsURILoader.cpp:191:8
    #14 0x7fee0ef21b3c in mozilla::net::HttpChannelChild::DoOnStartRequest(nsIRequest*, nsISupports*) src/netwerk/protocol/http/HttpChannelChild.cpp:707:20
    #15 0x7fee0ef3017a in mozilla::net::HttpChannelChild::OnStartRequest(nsresult const&, mozilla::net::nsHttpResponseHead const&, bool const&, mozilla::net::nsHttpHeaderArray const&, mozilla::net::ParentLoadInfoForwarderArgs const&, bool const&, bool const&, bool const&, unsigned long const&, int const&, unsigned int const&, nsTString<char> const&, nsTString<char> const&, mozilla::net::NetAddr const&, mozilla::net::NetAddr const&, unsigned int const&, nsTString<char> const&, long const&, bool const&, bool const&, bool const&, mozilla::net::ResourceTimingStructArgs const&, bool const&, mozilla::Maybe<unsigned int> const&, bool const&, nsILoadInfo::CrossOriginOpenerPolicy const&) src/netwerk/protocol/http/HttpChannelChild.cpp:557:3
    #16 0x7fee0efe6c56 in operator() src/netwerk/protocol/http/HttpChannelChild.cpp:411:15
    #17 0x7fee0efe6c56 in std::_Function_handler<void (), mozilla::net::HttpChannelChild::RecvOnStartRequest(nsresult const&, mozilla::net::nsHttpResponseHead const&, bool const&, mozilla::net::nsHttpHeaderArray const&, mozilla::net::ParentLoadInfoForwarderArgs const&, bool const&, bool const&, bool const&, unsigned long const&, int const&, unsigned int const&, nsTString<char> const&, nsTString<char> const&, mozilla::net::NetAddr const&, mozilla::net::NetAddr const&, short const&, unsigned int const&, nsTString<char> const&, long const&, bool const&, bool const&, bool const&, mozilla::net::ResourceTimingStructArgs const&, bool const&, mozilla::Maybe<unsigned int> const&, bool const&, nsILoadInfo::CrossOriginOpenerPolicy const&)::$_5>::_M_invoke(std::_Any_data const&) /builds/worker/fetches/clang/bin/../lib/gcc/x86_64-unknown-linux-gnu/7.4.0/../../../../include/c++/7.4.0/bits/std_function.h:316:2
    #18 0x7fee0ed60eda in mozilla::net::ChannelEventQueue::RunOrEnqueue(mozilla::net::ChannelEvent*, bool) src/obj-firefox/dist/include/mozilla/net/ChannelEventQueue.h:262:10
    #19 0x7fee0ef2e2bf in mozilla::net::HttpChannelChild::RecvOnStartRequest(nsresult const&, mozilla::net::nsHttpResponseHead const&, bool const&, mozilla::net::nsHttpHeaderArray const&, mozilla::net::ParentLoadInfoForwarderArgs const&, bool const&, bool const&, bool const&, unsigned long const&, int const&, unsigned int const&, nsTString<char> const&, nsTString<char> const&, mozilla::net::NetAddr const&, mozilla::net::NetAddr const&, short const&, unsigned int const&, nsTString<char> const&, long const&, bool const&, bool const&, bool const&, mozilla::net::ResourceTimingStructArgs const&, bool const&, mozilla::Maybe<unsigned int> const&, bool const&, nsILoadInfo::CrossOriginOpenerPolicy const&) src/netwerk/protocol/http/HttpChannelChild.cpp:401:12
    #20 0x7fee0fcc6e3c in mozilla::net::PHttpChannelChild::OnMessageReceived(IPC::Message const&) src/obj-firefox/ipc/ipdl/PHttpChannelChild.cpp:862:28
    #21 0x7fee0f9e1027 in mozilla::dom::PContentChild::OnMessageReceived(IPC::Message const&) src/obj-firefox/ipc/ipdl/PContentChild.cpp:8325:32
Flags: in-testsuite?

Huh, this looks like a nsFrameSelection which was not disconnected from its presshell before it went away.

I wonder if this is a recent-ish regression...

Is there a pernosco trace for this? Otherwise I can try to repro on monday or so.

Flags: needinfo?(emilio)
Flags: needinfo?(emilio)

Ah, I think we're not doing pernosco for sec bugs yet... Anyhow I'll poke.

Flags: needinfo?(emilio)

Yeah sorry no Pernosco sessions yet, the process is moving forward though :)

This seems like a fragmentation bug. On a debug build I hit:

[rr 133587 263194]Assertion failure: false (MOZ_ASSERT_UNREACHABLE: Frame property OverflowProperty should never be destroyed by the FrameProperties class), at /home/emilio/src/moz/gecko/layout/generic/nsContainerFrame.h:444

From a stack like:

#15 0x00007f70a5b89762 in nsContainerFrame::AssertOnDestroyingPropertyOverflowProperty(nsFrameList*) () at /home/emilio/src/moz/gecko/layout/generic/nsContainerFrame.h:444
#16 mozilla::FramePropertyDescriptor<nsFrameList>::Destruct<&nsContainerFrame::AssertOnDestroyingPropertyOverflowProperty>(void*) (aPropertyValue=<optimized out>) at /home/emilio/src/moz/gecko/layout/base/FrameProperties.h:91
#17 0x00007f709f9a5e44 in mozilla::FrameProperties::PropertyValue::DestroyValueFor(nsIFrame const*) (this=0x606000284078, aFrame=<optimized out>) at /home/emilio/src/moz/gecko/layout/base/FrameProperties.h:336
#18 mozilla::FrameProperties::SetInternal(mozilla::FramePropertyDescriptorUntyped const*, void*, nsIFrame const*)::{lambda(mozilla::FrameProperties::PropertyValue&)#1}::operator()(mozilla::FrameProperties::PropertyValue&) const
    (this=0x7ffdfc7c9930, aPV=...) at /home/emilio/src/moz/gecko/layout/base/FrameProperties.h:394
#19 nsTArray_Impl<mozilla::FrameProperties::PropertyValue, nsTArrayInfallibleAllocator>::InvokeWithIndexAndOrReferenceHelper<mozilla::FrameProperties::PropertyValue, mozilla::FrameProperties::PropertyValue&, void>::Invoke<mozilla::FrameProperties::SetInternal(mozilla::FramePropertyDescriptorUntyped const*, void*, nsIFrame const*)::{lambda(mozilla::FrameProperties::PropertyValue&)#1}>(mozilla::FrameProperties::SetInternal(mozilla::FramePropertyDescriptorUntyped const*, void*, nsIFrame const*)::{lambda(mozilla::FrameProperties::PropertyValue&)#1}&&, unsigned long, mozilla::FrameProperties::PropertyValue&) (f=..., e=...) at /home/emilio/src/moz/gecko/obj-asan-debug/dist/include/nsTArray.h:1920
#20 0x00007f70a5b3e144 in mozilla::FrameProperties::SetInternal(mozilla::FramePropertyDescriptorUntyped const*, void*, nsIFrame const*) (this=<optimized out>, aProperty=<optimized out>, aValue=<optimized out>, aFrame=0x6250004b8798)
    at /home/emilio/src/moz/gecko/layout/base/FrameProperties.h:391
#21 mozilla::FrameProperties::Set<nsFrameList>(mozilla::FramePropertyDescriptor<nsFrameList> const*, mozilla::detail::FramePropertyTypeHelper<nsFrameList>::Type, nsIFrame const*)
    (this=<optimized out>, aProperty=<optimized out>, aValue=<optimized out>, aFrame=0x6250004b8798) at /home/emilio/src/moz/gecko/layout/base/FrameProperties.h:160
#22 nsIFrame::SetProperty<nsFrameList>(mozilla::FramePropertyDescriptor<nsFrameList> const*, mozilla::detail::FramePropertyTypeHelper<nsFrameList>::Type) (this=0x6250004b8798, aProperty=<optimized out>, aValue=<optimized out>)
    at /home/emilio/src/moz/gecko/layout/generic/nsIFrame.h:3671
#23 nsContainerFrame::SetOverflowFrames(nsFrameList const&) (this=<optimized out>, aOverflowFrames=...) at /home/emilio/src/moz/gecko/layout/generic/nsContainerFrame.cpp:1405
#24 0x00007f70a5b4f3f3 in nsContainerFrame::PushChildrenToOverflow(nsIFrame*, nsIFrame*) (this=<optimized out>, aFromChild=<optimized out>, aPrevSibling=<optimized out>)
    at /home/emilio/src/moz/gecko/layout/generic/nsContainerFrame.cpp:1438
#25 0x00007f70a5d01a4e in nsInlineFrame::PushFrames(nsPresContext*, nsIFrame*, nsIFrame*, nsInlineFrame::InlineReflowInput&) (this=0x2, aPresContext=<optimized out>, aFromChild=0x7ffdfc7c7170, aPrevSibling=0xcf, aState=...)
    at /home/emilio/src/moz/gecko/layout/generic/nsInlineFrame.cpp:787
#26 0x00007f70a5d00f18 in nsInlineFrame::ReflowInlineFrame(nsPresContext*, mozilla::ReflowInput const&, nsInlineFrame::InlineReflowInput&, nsIFrame*, nsReflowStatus&)
    (this=<optimized out>, aPresContext=<optimized out>, aReflowInput=..., irs=..., aFrame=<optimized out>, aStatus=...) at /home/emilio/src/moz/gecko/layout/generic/nsInlineFrame.cpp:716
#27 0x00007f70a5cffc31 in nsInlineFrame::ReflowFrames(nsPresContext*, mozilla::ReflowInput const&, nsInlineFrame::InlineReflowInput&, mozilla::ReflowOutput&, nsReflowStatus&)
    (this=<optimized out>, aPresContext=<optimized out>, aReflowInput=..., irs=..., aMetrics=..., aStatus=...) at /home/emilio/src/moz/gecko/layout/generic/nsInlineFrame.cpp:582
#28 0x00007f70a5cfe401 in nsInlineFrame::Reflow(nsPresContext*, mozilla::ReflowOutput&, mozilla::ReflowInput const&, nsReflowStatus&) (this=0x6250004b8798, aPresContext=<optimized out>, aMetrics=..., aReflowInput=..., aStatus=...)
    at /home/emilio/src/moz/gecko/layout/generic/nsInlineFrame.cpp:363
#29 0x00007f70a5d806b4 in nsLineLayout::ReflowFrame(nsIFrame*, nsReflowStatus&, mozilla::ReflowOutput*, bool&)
    (this=<optimized out>, aFrame=0x6250004b8798, aReflowStatus=..., aMetrics=<optimized out>, aPushedFrame=@0x7ffdfc7ca670: false) at /home/emilio/src/moz/gecko/layout/generic/nsLineLayout.cpp:878
#30 0x00007f70a5b081f1 in nsBlockFrame::ReflowInlineFrame(mozilla::BlockReflowInput&, nsLineLayout&, nsLineList_iterator, nsIFrame*, LineReflowStatus*)
    (this=<optimized out>, aState=..., aLineLayout=..., aLine=..., aFrame=0x2, aLineReflowStatus=<optimized out>) at /home/emilio/src/moz/gecko/layout/generic/nsBlockFrame.cpp:4478
#31 0x00007f70a5b06959 in nsBlockFrame::DoReflowInlineFrames(mozilla::BlockReflowInput&, nsLineLayout&, nsLineList_iterator, nsFlowAreaRect&, int&, nsFloatManager::SavedState*, bool*, LineReflowStatus*, bool)
    (this=<optimized out>, aState=..., aLineLayout=..., aLine=..., aFloatAvailableSpace=..., aAvailableSpaceBSize=<optimized out>, aFloatStateBeforeLine=<optimized out>, aKeepReflowGoing=<optimized out>, aLineReflowStatus=<optimized out>, aAllowPullUp=<optimized out>) at /home/emilio/src/moz/gecko/layout/generic/nsBlockFrame.cpp:4280
#32 0x00007f70a5afd113 in nsBlockFrame::ReflowInlineFrames(mozilla::BlockReflowInput&, nsLineList_iterator, bool*) (this=<optimized out>, aState=..., aLine=..., aKeepReflowGoing=<optimized out>)
    at /home/emilio/src/moz/gecko/layout/generic/nsBlockFrame.cpp:4165
#33 0x00007f70a5af34da in nsBlockFrame::ReflowLine(mozilla::BlockReflowInput&, nsLineList_iterator, bool*) (this=<optimized out>, aState=..., aLine=..., aKeepReflowGoing=<optimized out>)
    at /home/emilio/src/moz/gecko/layout/generic/nsBlockFrame.cpp:3146
#34 0x00007f70a5ae7502 in nsBlockFrame::ReflowDirtyLines(mozilla::BlockReflowInput&) (this=<optimized out>, aState=...) at /home/emilio/src/moz/gecko/layout/generic/nsBlockFrame.cpp:2686
#35 0x00007f70a5ade118 in nsBlockFrame::Reflow(nsPresContext*, mozilla::ReflowOutput&, mozilla::ReflowInput const&, nsReflowStatus&) (this=0x6250004b8688, aPresContext=<optimized out>, aMetrics=..., aReflowInput=..., aStatus=...)

The existing overflow list has a (mozilla::ColumnSetWrapperFrame *) and co, but also a nsTextControlFrame*:

(rr) p $6
$17 = (nsFrameList *) 0x625000503540
(rr) p $6[0]
$18 = {mFirstChild = 0x625000503478, mLastChild = 0x625000502b98}
(rr) p $6[0].mFirstChild
$19 = (mozilla::ColumnSetWrapperFrame *) 0x625000503478
(rr) p $6[0].mFirstChild->DumpFrameTreeLimited()
ColumnSetWrapper(q)(1)@625000503478 parent=6250004b8798 next=6250004b8ec0 prev-in-flow=6250004b8998 (0,0,0,0) wm=sw-lr-ltr logical-size=(0 x 0) [state=0000060000c00606] [content=60d00005add0] [cs=61100020f488]<
>
$20 = void
(rr) p $6[0].mFirstChild->mNextSibling
$21 = (nsTextFrame *) 0x6250004b8ec0
(rr) p $6[0].mFirstChild->mNextSibling->DumpFrameTreeLimited()
Text(2)"\n"@6250004b8ec0 parent=6250004b8798 next=6250004b8f60 (12295,0,305,1140) [state=40010000a0000000] [content=60c0002a1a80] [cs=61100020fc08:-moz-text] [run=60c0002a4900][0,1,T] 
$22 = void
(rr) p $6[0].mFirstChild->mNextSibling->mNextSibling
$23 = (nsTextControlFrame *) 0x6250004b8f60
(rr) p $6[0].mFirstChild->mNextSibling->mNextSibling->DumpFrameTreeLimited()
nsTextControlFrame@6250004b8f60 parent=6250004b8798 next=625000502b98 (12600,-1560,9640,2640) [state=0002000000004210] [content=6120000a6e40] [cs=61100020f988]<
  HTMLScroll(div)(-1)@6250004b9050 parent=6250004b8f60 (60,60,9520,2520) [state=008b000000084008] [content=60d00005b2b0] [cs=6110001ee508]<
    ScrollbarFrame(scrollbar)(-1)@6250004b92a0 parent=6250004b9050 next=6250004b9588 (0,2520,9520,0) [state=000b000080c84008] [content=60d00005b380] [cs=6110001ee648]<
      SliderFrame(slider)(-1)@6250004b93b0 parent=6250004b92a0 (0,0,9520,0) [state=000b000080c04008] [content=60d00005bba0] [cs=61100022fb48]<
        ButtonBoxFrame(thumb)(0)@6250004b94b0 parent=6250004b93b0 (0,0,2760,0) [state=0009000080404000] [content=60d00005bc70] [cs=61100022fc88]<>
      >
    >
    ScrollbarFrame(scrollbar)(-1)@6250004b9588 parent=6250004b9050 next=625000502920 (9520,0,0,2520) [state=000b000080884008] [content=60d00005b450] [cs=6110001ee788]<
      SliderFrame(slider)(-1)@6250004b9698 parent=6250004b9588 (0,0,0,2520) vis-overflow=(0,0,0,0) scr-overflow=(0,0,0,2760) [state=000b000080804008] [content=60d00005c490] [cs=61100020e308]<
        ButtonBoxFrame(thumb)(0)@6250004b9798 parent=6250004b9698 (0,0,0,2760) [state=0009000080004000] [content=60d00005c560] [cs=61100020e448]<>
      >
    >
    Box(resizer)(-1)@625000502920 parent=6250004b9050 next=625000502a10 (8620,1620,900,900) [state=0009000080c04008] [content=60d00005b520] [cs=6110001ee8c8]<>
    Box(scrollcorner)(-1)@625000502a10 parent=6250004b9050 next=625000502ad8 (9520,2520,0,0) [state=0009000080c04208] [content=60d00005b5f0] [cs=61100022f788]<>
    Block(div)(-1)@625000502ad8 parent=6250004b9050 (0,0,9520,2520) [state=0009000000d04200] [content=60d00005b2b0] [cs=611000240808:-moz-scrolled-content]<
      line 625000502f60: count=1 state=inline,clean,prevmarginclean,not-impacted,not-wrapped,before:nobr,after:linebr[0x80100] (60,0,1,900) <
        Frame(br)(0)@625000502ee8 parent=625000502ad8 (60,0,1,900) [state=0000000000004000] [content=60d0000667e0] [cs=611000240d08]
      >
    >
  >
>
$24 = void
(rr) p $6[0].mFirstChild->mNextSibling->mNextSibling->mNextSibling
$25 = (nsTextFrame *) 0x625000502b98
(rr) p $6[0].mFirstChild->mNextSibling->mNextSibling->mNextSibling->DumpFrameTreeLimited
$26 = {void (const nsIFrame * const)} 0x7f70a5c01b40 <nsIFrame::DumpFrameTreeLimited() const>
(rr) p $6[0].mFirstChild->mNextSibling->mNextSibling->mNextSibling->DumpFrameTreeLimited()
Text(4)"\n"@625000502b98 parent=6250004b8798 (22240,0,0,1140) [state=40010000a0400000] [content=60c0002a1cc0] [cs=61100020fc08:-moz-text] [run=60c0002a49c0][0,1,T] 
$27 = void

Presumably leaking the nsTextControlFrame means it never gets destroyed and its TextControlState keeps a reference to an nsFrameSelection that points to a dead pres shell.

CC'ing some folks that may know more about inline layou and multicol.

Priority: -- → P1
Keywords: sec-high

I asked Mats to take a look at this.

@Ting-Yu: Would you mind keeping this on your radar if Mats has any actionable updates?

Flags: needinfo?(mats)
Flags: needinfo?(aethanyc)

(it crashes consistently in an ASAN or DEBUG build to be clear)

The root of the problem appears to be this code:
https://searchfox.org/mozilla-central/rev/174f1195ec740e8f17223b48018f7e14e6d4e40e/layout/generic/nsColumnSetFrame.cpp#865-888
which eventually leads to creating a ColumnSetWrapperFrame next-in-flow.
That seems wrong to me because the columnset in this case is an inline-block which are supposed to be atomic and thus never have any continuations in an unconstrained reflow.
I can't say that I understand how this code is supposed to work these days though.

Flags: needinfo?(mats)

(In reply to Mats Palmgren (:mats) from comment #9)

The root of the problem appears to be this code:
https://searchfox.org/mozilla-central/rev/174f1195ec740e8f17223b48018f7e14e6d4e40e/layout/generic/nsColumnSetFrame.cpp#865-888
which eventually leads to creating a ColumnSetWrapperFrame next-in-flow.
That seems wrong to me because the columnset in this case is an inline-block which are supposed to be atomic and thus never have any continuations in an unconstrained reflow.

If I understand how inline-blocks are made atomic correctly, we unconditionally set available block-size to NS_UNCONSTRAINEDSIZE [1] when a block is reflowing under an inline frame. However, when reflowing nsColumnSetFrame, we apply the columnized inline-block's block-size as the available block-size constraint in [2], and we didn't aware that the inline-block should be atomic.

I think the root cause is bug 534182. That is, we don't handle continuations in nsLineLayout very well (because we pass NS_UNCONSTRAINEDSIZE and assume the children should be fully complete.)

I think it should be reasonable to only apply [2] when ColumnSetWrapperFrame is not reflowed under nsLineLayout by checking whether ReflowInput::mLineLayout is nullptr or not.

[1] https://searchfox.org/mozilla-central/rev/df94cd5ba431234bc220ac081def0801fe44b89e/layout/generic/nsLineLayout.cpp#818
[2] https://searchfox.org/mozilla-central/rev/df94cd5ba431234bc220ac081def0801fe44b89e/layout/generic/nsBlockFrame.cpp#3717-3749
[3] https://searchfox.org/mozilla-central/rev/df94cd5ba431234bc220ac081def0801fe44b89e/layout/generic/ReflowInput.h#416

Because nsLineLayout::ReflowFrame() unconditionally sets its children's
available block-size to NS_UNCONSTRAINEDSIZE. ColumnSetWrapper should
respect that by not applying ColumnSetWrapper's block-size to
ColumnSet's available block-size when ColumnSetWrapper is reflowing
under nsLineLayout.

Assignee: nobody → aethanyc
Status: NEW → ASSIGNED
Flags: needinfo?(aethanyc)

Thanks Ting-Yu!

Flags: needinfo?(emilio)
Attachment #9127675 - Attachment description: Bug 1614101 - Avoid fragmenting inline-block multi-column containers. r?dbaron → Bug 1614101 - Make sure ColumnSet does not split in the last reflow if ColumnSetWrapper's available block-size is unconstrained.

I use mozregression to track down the regression. This was regressed by bug 1603088. (I'm not sure we want to set "Regressed by" field in a sec-high bug, so I'll leave blank for now).

Comment on attachment 9127675 [details]
Bug 1614101 - Make sure ColumnSet does not split in the last reflow if ColumnSetWrapper's available block-size is unconstrained.

Security Approval Request

  • How easily could an exploit be constructed based on the patch?: It's hard to deduce a test case from the patch. It requires the page to use balancing inline-block multicol layout with very small height/width (and probably with other conditions). This is unlikely to be used by any reasonable pages.
  • Do comments in the patch, the check-in comment, or tests included in the patch paint a bulls-eye on the security problem?: No
  • Which older supported branches are affected by this flaw?: Firefox 74
  • If not all supported branches, which bug introduced the flaw?: Bug 1603088
  • Do you have backports for the affected branches?: No
  • If not, how different, hard to create, and risky will they be?: The patch can be applied cleanly on Firefox 74 (currently the beta branch).
  • How likely is this patch to cause regressions; how much testing does it need?: It corrects one condition that can make multicol layout being fragmenting less, which is less error-prone. (Fragmenting layout frames tends to introduce more bugs ...)
Attachment #9127675 - Flags: sec-approval?
Has Regression Range: --- → yes

Comment on attachment 9127675 [details]
Bug 1614101 - Make sure ColumnSet does not split in the last reflow if ColumnSetWrapper's available block-size is unconstrained.

Please land and request uplift to mozilla-release for 74 RC2, thanks!

Attachment #9127675 - Flags: sec-approval? → sec-approval+
Attachment #9127676 - Flags: sec-approval? → sec-approval+
Flags: needinfo?(aethanyc)

Comment on attachment 9127675 [details]
Bug 1614101 - Make sure ColumnSet does not split in the last reflow if ColumnSetWrapper's available block-size is unconstrained.

Beta/Release Uplift Approval Request

  • User impact if declined: See my security approval request in Comment 15.
  • Is this code covered by automated tests?: Yes
  • Has the fix been verified in Nightly?: No
  • 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): It corrects one condition that can make multicol layout being fragmenting less, which is less error-prone.
  • String changes made/needed: None
Flags: needinfo?(aethanyc)
Attachment #9127675 - Flags: approval-mozilla-beta?

I update my patch to bump the assertion count for 1015844.html on Android. The crashtest involves multicol, and the assertion count was adjusted in bug 1603088. It is likely that my patch, which changes the behavior of multicol balancing, can make an impact on that again.

Yeah, the logcat just shows a bunch more instances of the already-occurring "Available block-size should be constrained because it's restricted..." assertion.

Comment on attachment 9127675 [details]
Bug 1614101 - Make sure ColumnSet does not split in the last reflow if ColumnSetWrapper's available block-size is unconstrained.

Approved for 74.0 RC2.

Attachment #9127675 - Flags: approval-mozilla-beta? → approval-mozilla-release+
Attachment #9127676 - Flags: approval-mozilla-beta? → approval-mozilla-release+
Group: layout-core-security → core-security-release
Status: ASSIGNED → RESOLVED
Closed: 5 years ago
Resolution: --- → FIXED
Target Milestone: --- → mozilla75
Crash Signature: [@ mozilla::layout::FrameChildListIterator::FrameChildListIterator]
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: