Skip to content

[WebGL] avoid 'forceContextLost' notifications during destruction#1588

Merged
magomez merged 1 commit intoWebPlatformForEmbedded:wpe-2.46from
emutavchi:wpe-2.46-webgl-crash
Dec 11, 2025
Merged

[WebGL] avoid 'forceContextLost' notifications during destruction#1588
magomez merged 1 commit intoWebPlatformForEmbedded:wpe-2.46from
emutavchi:wpe-2.46-webgl-crash

Conversation

@emutavchi
Copy link
Copy Markdown
Collaborator

GraphicsContextGLANGLE::reshape(0, 0) may trigger forceContextLost if it fails to reshape FBOs. If it happens during HTMLCanvasElement destruction, it will capture a reference to an HTMLCanvasElement that is being destroyed, and lead to a double destruction of HTMLCanvasElement and random crashes.

Here is callstack showing how forceLostContext captures a reference to HTMLCanvasElement during destruction:

(gdb) bt                                                                                                                                                                                         12:45:09 [99/1811#0  WebCore::HTMLCanvasElement::queueTaskKeepingObjectAlive (this=0xf2f8bd80, source=WebCore::TaskSource::WebGL, task=...) at ../git/Source/WebCore/html/HTMLCanvasElement.cpp:1001
#1  0xf66bd716 in WebCore::WebGLRenderingContextBase::scheduleTaskToDispatchContextLostEvent (this=this@entry=0xe06cce50) at ../lib32-recipe-sysroot/usr/include/c++/11.3.0/bits/unique_ptr.h:172
#2  0xf66cb5fc in WebCore::WebGLRenderingContextBase::forceLostContext (this=0xe06cce50, mode=mode@entry=WebCore::WebGLRenderingContextBase::RealLostContext)
    at ../git/Source/WebCore/html/canvas/WebGLRenderingContextBase.cpp:4664
#3  0xf66cb61e in WebCore::WebGLRenderingContextBase::forceContextLost (this=<optimized out>) at ../git/Source/WebCore/html/canvas/WebGLRenderingContextBase.cpp:5559
#4  0xf6a4c360 in WebCore::GraphicsContextGL::forceContextLost (this=<optimized out>) at ../git/Source/WebCore/platform/graphics/GraphicsContextGL.cpp:625
#5  0xf57168a2 in WebCore::GraphicsContextGLANGLE::reshape (this=0xc3033368, width=<optimized out>, height=<optimized out>) at ../git/Source/WebCore/platform/graphics/angle/GraphicsContextGLANGLE.cpp:755
#6  0xf66cc36c in WebCore::WebGLRenderingContextBase::destroyGraphicsContextGL (this=this@entry=0xe06cce50) at ../git/Source/WebCore/html/canvas/WebGLRenderingContextBase.cpp:704
#7  0xf66cc99a in WebCore::WebGLRenderingContextBase::~WebGLRenderingContextBase (this=this@entry=0xe06cce50, __in_chrg=<optimized out>) at ../git/Source/WebCore/html/canvas/WebGLRenderingContextBase.cpp:684
#8  0xf66cd4e4 in WebCore::WebGLRenderingContext::~WebGLRenderingContext (this=0xe06cce50, __in_chrg=<optimized out>) at ../git/Source/WebCore/html/canvas/WebGLRenderingContext.cpp:120
#9  0xf66d0a40 in non-virtual thunk to WebCore::WebGLRenderingContext::~WebGLRenderingContext() () from ./usr/lib/libWPEWebKit-1.1.so.0
#10 0xf65b157e in std::default_delete<WebCore::CanvasRenderingContext>::operator() (__ptr=<optimized out>, this=<optimized out>) at ../lib32-recipe-sysroot/usr/include/c++/11.3.0/bits/unique_ptr.h:79
#11 std::__uniq_ptr_impl<WebCore::CanvasRenderingContext, std::default_delete<WebCore::CanvasRenderingContext> >::reset (__p=0x0, this=0xf2f8be38)
    at ../lib32-recipe-sysroot/usr/include/c++/11.3.0/bits/unique_ptr.h:182
#12 std::unique_ptr<WebCore::CanvasRenderingContext, std::default_delete<WebCore::CanvasRenderingContext> >::reset (__p=0x0, this=0xf2f8be38)
    at ../lib32-recipe-sysroot/usr/include/c++/11.3.0/bits/unique_ptr.h:456
#13 std::unique_ptr<WebCore::CanvasRenderingContext, std::default_delete<WebCore::CanvasRenderingContext> >::operator=(decltype(nullptr)) (this=0xf2f8be38)
    at ../lib32-recipe-sysroot/usr/include/c++/11.3.0/bits/unique_ptr.h:397
#14 WebCore::HTMLCanvasElement::~HTMLCanvasElement (this=0xf2f8bd80, __in_chrg=<optimized out>) at ../git/Source/WebCore/html/HTMLCanvasElement.cpp:153
#15 0xf65b4c6e in WebCore::HTMLCanvasElement::operator delete (size=200, object=0xf2f8bd80) at ../git/Source/WebCore/html/HTMLCanvasElement.h:66
#16 WebCore::HTMLCanvasElement::~HTMLCanvasElement (this=0xf2f8bd80, __in_chrg=<optimized out>) at ../git/Source/WebCore/html/HTMLCanvasElement.cpp:155
#17 0xf6480d90 in WebCore::Node::removedLastRef (this=0xf2f8bd80) at ../git/Source/WebCore/dom/Node.cpp:2856
#18 0xf599b7ae in WebCore::Node::derefAllowingPartiallyDestroyed (this=<optimized out>) at ../git/Source/WebCore/dom/Node.h:883
#19 WebCore::Node::deref (this=<optimized out>) at ../git/Source/WebCore/dom/Node.h:863
#20 WebCore::EventTarget::deref (this=<optimized out>) at ../git/Source/WebCore/dom/Node.h:979
#21 WTF::DefaultRefDerefTraits<WebCore::EventTarget>::derefIfNotNull (ptr=<optimized out>) at WTF/Headers/wtf/Ref.h:62
#22 WTF::Ref<WebCore::EventTarget, WTF::RawPtrTraits<WebCore::EventTarget>, WTF::DefaultRefDerefTraits<WebCore::EventTarget> >::~Ref (this=<optimized out>, __in_chrg=<optimized out>) at WTF/Headers/wtf/Ref.h:82
#23 WebCore::JSDOMWrapper<WebCore::EventTarget, WTF::RawPtrTraits<WebCore::EventTarget> >::~JSDOMWrapper (this=<optimized out>, __in_chrg=<optimized out>) at ../git/Source/WebCore/bindings/js/JSDOMWrapper.h:74
#24 WebCore::JSEventTarget::~JSEventTarget (this=<optimized out>, __in_chrg=<optimized out>) at WebCore/DerivedSources/JSEventTarget.h:29
#25 WebCore::JSEventTarget::destroy (cell=<optimized out>) at WebCore/DerivedSources/JSEventTarget.cpp:198
#26 0xf53a972c in JSC::JSDestructibleObjectDestroyFunc::operator() (cell=<optimized out>, this=<optimized out>) at ../git/Source/JavaScriptCore/runtime/JSDestructibleObjectHeapCellType.cpp:43
#27 JSC::JSDestructibleObjectHeapCellType::destroy (this=<optimized out>, vm=..., cell=<optimized out>) at ../git/Source/JavaScriptCore/runtime/JSDestructibleObjectHeapCellType.cpp:61
#28 0xf50984f8 in JSC::Subspace::destroy (this=<optimized out>, vm=..., cell=cell@entry=0xdc6527c8) at ../git/Source/JavaScriptCore/heap/Subspace.cpp:66
#29 0xf5094f1e in JSC::PreciseAllocation::sweep (this=0xdc652788) at ../git/Source/JavaScriptCore/heap/PreciseAllocation.h:65
#30 JSC::PreciseAllocation::sweep (this=this@entry=0xdc652788) at ../git/Source/JavaScriptCore/heap/PreciseAllocation.cpp:270
#31 0xf5093fa0 in JSC::MarkedSpace::sweepPreciseAllocations (this=this@entry=0xdf9000e8) at ../git/Source/JavaScriptCore/heap/MarkedSpace.cpp:237
#32 0xf5069ae8 in JSC::Heap::sweepInFinalize (this=0xdf900088) at ../git/Source/JavaScriptCore/heap/Heap.cpp:2305
#33 JSC::Heap::finalize (this=this@entry=0xdf900088) at ../git/Source/JavaScriptCore/heap/Heap.cpp:2239
#34 0xf5069de2 in JSC::Heap::handleNeedFinalize (this=this@entry=0xdf900088, oldState=<optimized out>) at ../git/Source/JavaScriptCore/heap/Heap.cpp:2177
#35 0xf506d918 in JSC::Heap::handleNeedFinalize (this=<optimized out>) at ../git/Source/JavaScriptCore/heap/Heap.cpp:2188
#36 JSC::Heap::finishChangingPhase (this=this@entry=0xdf900088, conn=conn@entry=JSC::GCConductor::Mutator) at ../git/Source/JavaScriptCore/heap/Heap.cpp:1789
#37 0xf506e452 in JSC::Heap::changePhase (nextPhase=JSC::CollectorPhase::NotRunning, conn=JSC::GCConductor::Mutator, this=0xdf900088) at ../git/Source/JavaScriptCore/heap/Heap.cpp:1764
#38 JSC::Heap::runEndPhase (this=this@entry=0xdf900088, conn=conn@entry=JSC::GCConductor::Mutator) at ../git/Source/JavaScriptCore/heap/Heap.cpp:1754
#39 0xf5078e88 in JSC::Heap::runCurrentPhase (currentThreadState=0xff860e2c, conn=JSC::GCConductor::Mutator, this=0xdf900088) at ../git/Source/JavaScriptCore/heap/Heap.cpp:1397
#40 JSC::Heap::runCurrentPhase (this=0xdf900088, conn=<optimized out>, currentThreadState=0xff860e2c) at ../git/Source/JavaScriptCore/heap/Heap.cpp:1354
#41 0xf507a564 in operator() (state=..., __closure=0xff860e7c) at ../git/Source/JavaScriptCore/heap/Heap.cpp:2016
#42 WTF::ScopedLambdaFunctor<void(JSC::CurrentThreadState&), JSC::Heap::collectInMutatorThread()::<lambda(JSC::CurrentThreadState&)> >::implFunction(void *, JSC::CurrentThreadState &) (argument=0xff860e74,
    arguments#0=...) at WTF/Headers/wtf/ScopedLambda.h:106
#43 0xf508f182 in WTF::ScopedLambda<void(JSC::CurrentThreadState&)>::operator()<JSC::CurrentThreadState&> (this=<optimized out>) at WTF/Headers/wtf/ScopedLambda.h:56
#44 JSC::callWithCurrentThreadState (lambda=...) at ../git/Source/JavaScriptCore/heap/MachineStackMarker.cpp:227
#45 0xf5078ece in JSC::Heap::collectInMutatorThread (this=this@entry=0xdf900088) at ../lib32-recipe-sysroot/usr/include/c++/11.3.0/bits/move.h:77
#46 0xf5078f06 in JSC::Heap::stopIfNecessarySlow (oldState=5, this=0xdf900088) at ../git/Source/JavaScriptCore/heap/Heap.cpp:1997
#47 JSC::Heap::stopIfNecessarySlow (this=0xdf900088, oldState=5) at ../git/Source/JavaScriptCore/heap/Heap.cpp:1978
#48 0xf50793e2 in JSC::Heap::stopIfNecessarySlow (this=this@entry=0xdf900088) at ../git/Source/JavaScriptCore/heap/Heap.cpp:1969
#49 0xf5079968 in JSC::Heap::stopIfNecessary (this=0xdf900088) at ../git/Source/JavaScriptCore/heap/HeapInlines.h:264
#50 JSC::Heap::collectIfNecessaryOrDefer (this=0xdf900088, deferralContext=deferralContext@entry=0x0) at ../git/Source/JavaScriptCore/heap/Heap.cpp:2844

GraphicsContextGLANGLE::reshape(0, 0) may trigger `forceContextLost`
if it fails to reshape FBOs. If it happens during HTMLCanvasElement
destruction it will capture areference to an HTMLCanvasElement that is
being destroyed, and lead to a double destruction of HTMLCanvasElement
and random crashes.
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a crash during HTMLCanvasElement destruction where reshape(0,0) could trigger context loss notifications that capture a reference to the element being destroyed, leading to double destruction and crashes.

  • Reorders operations in destroyGraphicsContextGL() to set client to nullptr before calling reshape
  • Prevents forceContextLost callbacks during destruction that would queue tasks with references to the destructing canvas element

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@emutavchi
Copy link
Copy Markdown
Collaborator Author

attaching a simple test page where the browser is crashing because of "pure" function call during the destruction of 2d canvas context: webgl_test.html

callstack

Program terminated with signal SIGABRT, Aborted.
#0  __libc_do_syscall () at ../sysdeps/unix/sysv/linux/arm/libc-do-syscall.S:47
warning: 47     ../sysdeps/unix/sysv/linux/arm/libc-do-syscall.S: No such file or directory
[Current thread is 1 (process 34)]
(gdb) bt
#0  __libc_do_syscall () at ../sysdeps/unix/sysv/linux/arm/libc-do-syscall.S:47
#1  0xf3d1e13c in __pthread_kill_implementation (threadid=<optimized out>, signo=6, no_tid=<optimized out>) at pthread_kill.c:43
#2  0xf3cefb86 in __GI_raise (sig=sig@entry=6) at ../sysdeps/posix/raise.c:26
#3  0xf3ce15d4 in __GI_abort () at abort.c:79
#4  0xf2ac2edc in __gnu_cxx::__verbose_terminate_handler () at ../../../../../../../../work-shared/gcc-11.3.0-r0/gcc-11.3.0/libstdc++-v3/libsupc++/vterminate.cc:95
#5  0xf2ac1b0c in __cxxabiv1::__terminate (handler=<optimized out>) at ../../../../../../../../work-shared/gcc-11.3.0-r0/gcc-11.3.0/libstdc++-v3/libsupc++/eh_terminate.cc:48
#6  0xf2ac1b6e in std::terminate () at ../../../../../../../../work-shared/gcc-11.3.0-r0/gcc-11.3.0/libstdc++-v3/libsupc++/eh_terminate.cc:58
#7  0xf2ac247a in __cxxabiv1::__cxa_pure_virtual () at ../../../../../../../../work-shared/gcc-11.3.0-r0/gcc-11.3.0/libstdc++-v3/libsupc++/pure.cc:50
#8  0xf62a4f5c in WebCore::CanvasRenderingContext::deref (this=<optimized out>) at ../git/Source/WebCore/html/canvas/CanvasRenderingContext.cpp:87
#9  0xf5485356 in WTF::DefaultRefDerefTraits<WebCore::CanvasRenderingContext2D>::derefIfNotNull (ptr=<optimized out>) at WTF/Headers/wtf/Ref.h:62
#10 WTF::Ref<WebCore::CanvasRenderingContext2D, WTF::RawPtrTraits<WebCore::CanvasRenderingContext2D>, WTF::DefaultRefDerefTraits<WebCore::CanvasRenderingContext2D> >::~Ref (this=<optimized out>,
    __in_chrg=<optimized out>) at WTF/Headers/wtf/Ref.h:82
#11 WebCore::JSDOMWrapper<WebCore::CanvasRenderingContext2D, WTF::RawPtrTraits<WebCore::CanvasRenderingContext2D> >::~JSDOMWrapper (this=<optimized out>, __in_chrg=<optimized out>)
    at ../git/Source/WebCore/bindings/js/JSDOMWrapper.h:74
#12 WebCore::JSCanvasRenderingContext2D::~JSCanvasRenderingContext2D (this=<optimized out>, __in_chrg=<optimized out>) at WebCore/DerivedSources/JSCanvasRenderingContext2D.h:30
#13 WebCore::JSCanvasRenderingContext2D::destroy (cell=<optimized out>) at WebCore/DerivedSources/JSCanvasRenderingContext2D.cpp:456
#14 0xf4fc972c in JSC::JSDestructibleObjectDestroyFunc::operator() (cell=<optimized out>, this=<optimized out>) at ../git/Source/JavaScriptCore/runtime/JSDestructibleObjectHeapCellType.cpp:43
#15 JSC::JSDestructibleObjectHeapCellType::destroy (this=<optimized out>, vm=..., cell=<optimized out>) at ../git/Source/JavaScriptCore/runtime/JSDestructibleObjectHeapCellType.cpp:61
#16 0xf4cb84f8 in JSC::Subspace::destroy (this=<optimized out>, vm=..., cell=cell@entry=0xc696fd68) at ../git/Source/JavaScriptCore/heap/Subspace.cpp:66
#17 0xf4cb4f1e in JSC::PreciseAllocation::sweep (this=0xc696fd28) at ../git/Source/JavaScriptCore/heap/PreciseAllocation.h:65
#18 JSC::PreciseAllocation::sweep (this=this@entry=0xc696fd28) at ../git/Source/JavaScriptCore/heap/PreciseAllocation.cpp:270
#19 0xf4cb3fa0 in JSC::MarkedSpace::sweepPreciseAllocations (this=this@entry=0xdfd000e8) at ../git/Source/JavaScriptCore/heap/MarkedSpace.cpp:237
#20 0xf4c89ae8 in JSC::Heap::sweepInFinalize (this=0xdfd00088) at ../git/Source/JavaScriptCore/heap/Heap.cpp:2305

@magomez
Copy link
Copy Markdown

magomez commented Dec 11, 2025

This is a problem just I've just discovered on 2.38 while working on the page lifecycle freeze (despite it doesn't lead to a crash there). But I fixed it on 2.38 by reshaping to (1, 1) instead of (0, 0), so we can still have the early release of resources. I think we could do the same here.

@emutavchi
Copy link
Copy Markdown
Collaborator Author

But I fixed it on 2.38 by reshaping to (1, 1) instead of (0, 0), so we can still have the early release of resources. I think we could do the same here.

reshaping to (1,1) doesn't solve the underlying issue. It can still fail for some reason and call forceLostContext() on a canvas object that is being destroyed (and capture a reference to it), resulting in a crash.

@magomez
Copy link
Copy Markdown

magomez commented Dec 11, 2025

But I fixed it on 2.38 by reshaping to (1, 1) instead of (0, 0), so we can still have the early release of resources. I think we could do the same here.

reshaping to (1,1) doesn't solve the underlying issue. It can still fail for some reason and call forceLostContext() on a canvas object that is being destroyed (and capture a reference to it), resulting in a crash.

What a pity. Seems that the reshape is causing some trouble on 2.46 with ANGLE that doesn't happen on 2.38 without it. Ok, let's remove it then.

@magomez magomez self-requested a review December 11, 2025 13:30
@magomez magomez merged commit d15368c into WebPlatformForEmbedded:wpe-2.46 Dec 11, 2025
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

4 participants