CVE-2026-43742
Overview
Advisory: Apple Security Advisory
Impact:
Description: Processing maliciously crafted web content may lead to an unexpected process crash
Researchers: Юлия Мерцалова
| Attribute | Value |
|---|---|
| CVE | CVE-2026-43742 |
| Bugzilla | 315161 |
| Component | WebCore |
| Bug Class | UAF |
| Severity | medium |
| Commit | 034f2fbd9b69edba… |
| Advisory | Apple Advisory |
Root Cause Analysis
A use-after-free issue was addressed with improved memory management.
Files Changed
Source Files
Source/WebCore/html/canvas/WebGL2RenderingContext.hSource/WebCore/html/canvas/WebGLRenderingContext.hSource/WebCore/html/canvas/WebGLRenderingContextBase.cppSource/WebCore/html/canvas/WebGLRenderingContextBase.h
Test Files
LayoutTests/fast/canvas/webgl/context-restore-concurrent-gc-no-crash-expected.txtLayoutTests/fast/canvas/webgl/context-restore-concurrent-gc-no-crash.html
Patch Preview
diff --git a/LayoutTests/fast/canvas/webgl/context-restore-concurrent-gc-no-crash-expected.txt b/LayoutTests/fast/canvas/webgl/context-restore-concurrent-gc-no-crash-expected.txt
new file mode 100644
index 000000000000..c2541f4f3dd7
--- /dev/null
+++ b/LayoutTests/fast/canvas/webgl/context-restore-concurrent-gc-no-crash-expected.txt
@@ -0,0 +1 @@
+PASS if no crash.
diff --git a/LayoutTests/fast/canvas/webgl/context-restore-concurrent-gc-no-crash.html b/LayoutTests/fast/canvas/webgl/context-restore-concurrent-gc-no-crash.html
new file mode 100644
index 000000000000..a62c55af8994
--- /dev/null
+++ b/LayoutTests/fast/canvas/webgl/context-restore-concurrent-gc-no-crash.html
@@ -0,0 +1,66 @@
+<!DOCTYPE html>
+<html>
+<body>
+<!-- Exercises a race between WebGL2 context restoration (which reinitializes
+ bound-object state via initializeNewContext) and concurrent GC marking (which
+ traverses that state via addMembersToOpaqueRoots). Without objectGraphLock()
+ held during restoration, the restore path can free objects the GC marker is
+ still reading, causing a use-after-free. -->
+<script>
+if (window.testRunner) {
+ testRunner.dumpAsText();
+ testRunner.waitUntilDone();
+}
+if (window.internals)
+ internals.settings.setWebGLErrorsToConsoleEnabled(false);
+
+const tick = () => new Promise(r => setTimeout(r, 0));
+
+// Ensure the GC marker doesn't reach the WebGL wrapper until the concurrent phase.
+const heapPaddingSize = 300000;
+const heapPadding = new Array(heapPaddingSize);
+for (let i = 0; i < heapPaddingSize; i++)
+ heapPadding[i] = { a: i, b: { c: i } };
+
+async function loseAndRestoreContext() {
+ const canvas = document.body.appendChild(document.createElement('canvas'));
+ canvas.width = 1;
+ canvas.height = 1;
+ const gl = canvas.getContext('webgl2');
+ const ext = gl.getExtension('WEBGL_lose_context');
+ for (let i = 0; i < heapPaddingSize; i += 4096)
+ heapPadding[i].g = gl;
+
+ const contextLost = new Promise(r => {
+ canvas.addEventListener('webglcontextlost', e => { e.preventDefault(); r(); });
+ });
+ const contextRestored = new Promise(r => {
+ canvas.addEventListener('webglcontextrestored', () => r());
+ });
+
+ ext.loseContext();
+ await contextLost;
+ await tick();
+
+ // Trigger GC so its concurrent marking phase overlaps the restore timer.
+ new WebAssembly.Memory({ initial: 1024 });
+ new WebAssembly.Memory({ initial: 1024 });
+ ext.restoreContext();
+ await tick();
+ await tick();
+ await contextRestored;
+
+ canvas.remove();
+}
+
+async function runTest() {
+ for (let i = 0; i < 50; i++)
+ await loseAndRestoreContext();
+ document.body.textContent = 'PASS if no crash.';
+ if (window.testRunner)
+ testRunner.notifyDone();
+}
+runTest();
+</script>
+</body>
+</html>
diff --git a/Source/WebCore/html/canvas/WebGL2RenderingContext.h b/Source/WebCore/html/canvas/WebGL2RenderingContext.h
index dcf736d39056..b691cd7a4c8b 100644
--- a/Source/WebCore/html/canvas/WebGL2RenderingContext.h
+++ b/Source/WebCore/html/canvas/WebGL2RenderingContext.h
@@ -261,7 +261,7 @@ class WebGL2RenderingContext final : public WebGLRenderingContextBase {
private:
using WebGLRenderingContextBase::WebGLRenderingContextBase;
- void initializeContextState() final;
+ void initializeContextState() WTF_REQUIRES_LOCK(objectGraphLock()) final;
RefPtr<ArrayBufferView> arrayBufferViewSliceFactory(ASCIILiteral functionName, const ArrayBufferView& data, unsigned startByte, unsigned bytelength);
RefPtr<ArrayBufferView> sliceArrayBufferView(ASCIILiteral functionName, const ArrayBufferView& data, GCGLuint srcOffset, GCGLuint length);
@@ -269,7 +269,7 @@ class WebGL2RenderingContext final : public WebGLRenderingContextBase {
long long getInt64Parameter(GCGLenum) final;
Vector<bool> getIndexedBooleanArrayParameter(GCGLenum pname, GCGLuint index);
- void initializeDefaultObjects() final;
+ void initializeDefaultObjects() WTF_REQUIRES_LOCK(objectGraphLock()) final;
void detachAndRemoveAllObjects() WTF_REQUIRES_LOCK(objectGraphLock()) final;
bool validateBufferTarget(ASCIILiteral functionName, GCGLenum target) final;
bool validateBufferTargetCompatibility(ASCIILiteral, GCGLenum, WebGLBuffer*);
diff --git a/Source/WebCore/html/canvas/WebGLRenderingContext.h b/Source/WebCore/html/canvas/WebGLRenderingContext.h
index 7404a87eb9a4..a9b2f1b3e9de 100644
--- a/Source/WebCore/html/canvas/WebGLRenderingContext.h
+++ b/Source/WebCore/html/canvas/WebGLRenderingContext.h
@@ -60,7 +60,7 @@ class WebGLRenderingContext final : public WebGLRenderingContextBase {
private:
using WebGLRenderingContextBase::WebGLRenderingContextBase;
- void initializeDefaultObjects() final;
+ void initializeDefaultObjects() WTF_REQUIRES_LOCK(objectGraphLock()) final;
void detachAndRemoveAllObjects() WTF_REQUIRES_LOCK(objectGraphLock()) final;
};
diff --git a/Source/WebCore/html/canvas/WebGLRenderingContextBase.cpp b/Source/WebCore/html/canvas/WebGLRenderingContextBase.cpp
index f128a2833781..2216b3ef9011 100644
--- a/Source/WebCore/html/canvas/WebGLRenderingContextBase.cpp
+++ b/Source/WebCore/html/canvas/WebGLRenderingContextBase.cpp
@@ -524,8 +524,11 @@ void WebGLRenderingContextBase::initializeNewContext(Ref<GraphicsContextGL> cont
updateActiveOrdinal();
if (!wasActive)
addActiveContext(*this);
- initializeContextState();
- initializeDefaultObjects();
+ {
+ Locker locker { objectGraphLock() };
+ initializeContextState();
+ initializeDefaultObjects();
+ }
// Next calls will receive the context lost callback.
m_context->setClient(this);
}
diff --git a/Source/WebCore/html/canvas/WebGLRenderingContextBase.h b/Source/WebCore/html/canvas/WebGLRenderingContextBase.h
index 57fc93560676..9313e4ff0bca 100644
--- a/Source/WebCore/html/canvas/WebGLRenderingContextBase.h
+++ b/Source/WebCore/html/canvas/WebGLRenderingContextBase.h
@@ -538,8 +538,8 @@ class WebGLRenderingContextBase : public GraphicsContextGL::Client, public GPUBa
friend class ScopedWebGLRestoreTexture;
void initializeNewContext(Ref<GraphicsContextGL>);
- virtual void initializeContextState();
- virtual void initializeDefaultObjects();
+ virtual void initializeContextState() WTF_REQUIRES_LOCK(objectGraphLock());
+ virtual void initializeDefaultObjects() WTF_REQUIRES_LOCK(objectGraphLock());
virtual void detachAndRemoveAllObjects() WTF_REQUIRES_LOCK(objectGraphLock());
// ActiveDOMObject