CVE-2026-43709
Overview
Advisory: Apple Security Advisory
Impact:
Description: Processing maliciously crafted web content may lead to an unexpected process crash
| Attribute | Value |
|---|---|
| CVE | CVE-2026-43709 |
| Bugzilla | 314398 |
| Component | WebKit |
| Bug Class | UAF |
| Severity | medium |
| Commit | 9d2cc8c9895d51eb… |
| Advisory | Apple Advisory |
Root Cause Analysis
A use-after-free issue was addressed with improved memory management.
Files Changed
Source Files
Source/ThirdParty/ANGLE/src/libANGLE/renderer/metal/ContextMtl.hSource/ThirdParty/ANGLE/src/libANGLE/renderer/metal/ContextMtl.mmSource/ThirdParty/ANGLE/src/libANGLE/renderer/metal/QueryMtl.hSource/ThirdParty/ANGLE/src/libANGLE/renderer/metal/QueryMtl.mmSource/ThirdParty/ANGLE/src/libANGLE/renderer/metal/mtl_command_buffer.mmSource/ThirdParty/ANGLE/src/libANGLE/renderer/metal/mtl_occlusion_query_pool.hSource/ThirdParty/ANGLE/src/libANGLE/renderer/metal/mtl_occlusion_query_pool.mmSource/ThirdParty/ANGLE/src/libANGLE/renderer/metal/mtl_render_utils.hSource/ThirdParty/ANGLE/src/libANGLE/renderer/metal/mtl_render_utils.mm
Test Files
LayoutTests/platform/mac-wk2/TestExpectationsSource/ThirdParty/ANGLE/src/tests/gl_tests/OcclusionQueriesTest.cpp
Patch Preview
diff --git a/LayoutTests/platform/mac-wk2/TestExpectations b/LayoutTests/platform/mac-wk2/TestExpectations
index 5661950f388b..5066d4b54544 100644
--- a/LayoutTests/platform/mac-wk2/TestExpectations
+++ b/LayoutTests/platform/mac-wk2/TestExpectations
@@ -2309,6 +2309,8 @@ webkit.org/b/315877 [ Sequoia Debug x86_64 ] webgl/2.0.y/conformance2/textures/m
webkit.org/b/315877 [ Sequoia Debug x86_64 ] webgl/2.0.y/conformance2/textures/webgl_canvas/tex-3d-r11f_g11f_b10f-rgb-float.html [ Pass Timeout ]
webkit.org/b/315877 [ Sequoia Debug x86_64 ] webgl/2.0.y/conformance2/textures/image_bitmap_from_video/tex-2d-rgba32f-rgba-float.html [ Pass Failure ]
+webkit.org/b/318751 [ Debug arm64 ] webgl/2.0.y/conformance2/textures/misc/tex-unpack-params.html [ Pass Failure ]
+
webkit.org/b/315881 [ Sequoia Release x86_64 ] fast/attachment/cocoa/wide-attachment-class.html [ Pass ImageOnlyFailure ]
webkit.org/b/315881 [ Sequoia Release x86_64 ] fast/attachment/cocoa/wide-attachment-default-icon.html [ Pass ImageOnlyFailure ]
webkit.org/b/315881 [ Sequoia Release x86_64 ] fast/attachment/cocoa/wide-attachment-folder-icon.html [ Pass ImageOnlyFailure ]
diff --git a/Source/ThirdParty/ANGLE/src/libANGLE/renderer/metal/ContextMtl.h b/Source/ThirdParty/ANGLE/src/libANGLE/renderer/metal/ContextMtl.h
index 41553c65283a..a85c8ecffa9f 100644
--- a/Source/ThirdParty/ANGLE/src/libANGLE/renderer/metal/ContextMtl.h
+++ b/Source/ThirdParty/ANGLE/src/libANGLE/renderer/metal/ContextMtl.h
@@ -31,6 +31,7 @@ namespace rx
{
class DisplayMtl;
class FramebufferMtl;
+class QueryMtl;
class VertexArrayMtl;
class ProgramMtl;
class ProgramExecutableMtl;
@@ -301,20 +302,22 @@ class ContextMtl : public ContextImpl, public mtl::Context
bool renderPassChanged);
void onBackbufferResized(const gl::Context *context, WindowSurfaceMtl *backbuffer);
- // Invoke by QueryMtl
- angle::Result onOcclusionQueryBegin(const gl::Context *context, QueryMtl *query);
- void onOcclusionQueryEnd(const gl::Context *context, QueryMtl *query);
- void onOcclusionQueryDestroy(const gl::Context *context, QueryMtl *query);
+ angle::Result onOcclusionQueryBegin(QueryMtl &query);
+ void onOcclusionQueryEnd();
+ void onOcclusionQueryDestroy(QueryMtl &query);
// Useful for temporarily pause then restart occlusion query during clear/blit with draw.
- bool hasActiveOcclusionQuery() const { return mOcclusionQuery; }
+ bool isOcclusionQueryEnabledInRenderPass() const
+ {
+ return mOcclusionQueryIsEnabledInRenderPass;
+ }
// Disable the occlusion query in the current render pass.
// The render pass must already started.
- void disableActiveOcclusionQueryInRenderPass();
+ void disableOcclusionQueryInRenderPass();
// Re-enable the occlusion query in the current render pass.
// The render pass must already started.
// NOTE: the old query's result will be retained and combined with the new result.
- angle::Result restartActiveOcclusionQueryInRenderPass();
+ angle::Result enableOcclusionQueryInRenderPass();
// Invoke by TransformFeedbackMtl
void onTransformFeedbackActive(const gl::Context *context, TransformFeedbackMtl *xfb);
@@ -536,8 +539,6 @@ class ContextMtl : public ContextImpl, public mtl::Context
bool xfbPass,
bool *pipelineDescChanged);
- angle::Result startOcclusionQueryInRenderPass(QueryMtl *query, bool clearOldValue);
-
angle::Result checkCommandBufferError();
// Dirty bits.
@@ -607,7 +608,6 @@ class ContextMtl : public ContextImpl, public mtl::Context
FramebufferMtl *mDrawFramebuffer = nullptr;
VertexArrayMtl *mVertexArray = nullptr;
ProgramExecutableMtl *mExecutable = nullptr;
- QueryMtl *mOcclusionQuery = nullptr;
using DirtyBits = angle::BitSet<DIRTY_BIT_MAX>;
@@ -658,6 +658,9 @@ class ContextMtl : public ContextImpl, public mtl::Context
id<MTLTexture> mRasterizationRateMapTexture;
mtl::ContextDevice mContextDevice;
+
+ mtl::BufferRef mOcclusionQueryResultBuffer;
+ bool mOcclusionQueryIsEnabledInRenderPass{false};
};
} // namespace rx
diff --git a/Source/ThirdParty/ANGLE/src/libANGLE/renderer/metal/ContextMtl.mm b/Source/ThirdParty/ANGLE/src/libANGLE/renderer/metal/ContextMtl.mm
index 644b213beee9..8703f6c2d578 100644
--- a/Source/ThirdParty/ANGLE/src/libANGLE/renderer/metal/ContextMtl.mm
+++ b/Source/ThirdParty/ANGLE/src/libANGLE/renderer/metal/ContextMtl.mm
@@ -1823,7 +1823,7 @@ GLint GetOwnershipIdentity(const egl::AttributeMap &attribs)
mRenderEncoder.setStoreAction(MTLStoreActionStore);
}
- disableActiveOcclusionQueryInRenderPass();
+ disableOcclusionQueryInRenderPass();
mOcclusionQueryPool.prepareRenderPassVisibilityPoolBuffer(this);
@@ -2281,84 +2281,67 @@ GLint GetOwnershipIdentity(const egl::AttributeMap &attribs)
onDrawFrameBufferChangedState(context, framebuffer, true);
}
-angle::Result ContextMtl::onOcclusionQueryBegin(const gl::Context *context, QueryMtl *query)
+angle::Result ContextMtl::onOcclusionQueryBegin(QueryMtl &query)
{
- ASSERT(mOcclusionQuery == nullptr);
- mOcclusionQuery = query;
-
+ ASSERT(!mOcclusionQueryResultBuffer); // Frontend guarantees none active at the time.
+ const mtl::BufferRef &resultBuffer = query.getVisibilityResultBuffer();
+ bool isEnabledInRenderPass;
if (mRenderEncoder.valid())
{
- // if render pass has started, start the query in the encoder
- return startOcclusionQueryInRenderPass(query, true);
+ size_t resultOffset;
+ ANGLE_TRY(mOcclusionQueryPool.beginQuery(this, resultBuffer, &resultOffset));
+ mRenderEncoder.setVisibilityResultMode(MTLVisibilityResultModeBoolean, resultOffset);
+ // Result is available after flush.
+ mCmdBuffer.setWriteDependency(resultBuffer, /*isRenderCommand=*/true);
+ isEnabledInRenderPass = true;
}
else
{
- query->resetVisibilityResult(this);
+ // Reset the occlusion query result stored in buffer to zero.
+ // Later draws will use continueQuery() to enable the visibility buffer writes.
+ auto blitEncoder = getBlitCommandEncoder();
+ blitEncoder->fillBuffer(resultBuffer, NSMakeRange(0, mtl::kOcclusionQueryResultSize), 0);
+ resultBuffer->syncContent(this, blitEncoder);
+ isEnabledInRenderPass = false;
}
-
+ mOcclusionQueryResultBuffer = resultBuffer;
+ mOcclusionQueryIsEnabledInRenderPass = isEnabledInRenderPass;
return angle::Result::Continue;
}
-void ContextMtl::onOcclusionQueryEnd(const gl::Context *context, QueryMtl *query)
-{
- ASSERT(mOcclusionQuery == query);
-
- if (mRenderEncoder.valid())
- {
- // if render pass has started, end the query in the encoder
- disableActiveOcclusionQueryInRenderPass();
- }
- mOcclusionQuery = nullptr;
-}
-void ContextMtl::onOcclusionQueryDestroy(const gl::Context *context, QueryMtl *query)
+void ContextMtl::onOcclusionQueryEnd()
{
- if (query->getAllocatedVisibilityOffsets().empty())
- {
- return;
- }
- if (mOcclusionQuery == query)
- {
- onOcclusionQueryEnd(context, query);
- }
- mOcclusionQueryPool.deallocateQueryOffset(this, query);
+ ASSERT(mOcclusionQueryResultBuffer); // Frontend guarantees one active at the time.
+ disableOcclusionQueryInRenderPass();
+ mOcclusionQueryResultBuffer = nullptr;
}
-void ContextMtl::disableActiveOcclusionQueryInRenderPass()
+void ContextMtl::onOcclusionQueryDestroy(QueryMtl &query)
{
- if (!mOcclusionQuery || mOcclusionQuery->getAllocatedVisibilityOffsets().empty())
- {
- return;
- }
-
- ASSERT(mRenderEncoder.valid());
- mRenderEncoder.setVisibilityResultMode(MTLVisibilityResultModeDisabled,
- mOcclusionQuery->getAllocatedVisibilityOffsets().back());
+ // On normal operation frontend guaraantees that end is called before destroy.
+ // On context destruction, active query is destroyed without end.
+ // Discard is valid for both.
+ mOcclusionQueryPool.discardQuery(query.getVisibilityResultBuffer());
}
-angle::Result ContextMtl::restartActiveOcclusionQueryInRenderPass()
+void ContextMtl::disableOcclusionQueryInRenderPass()
{
- if (!mOcclusionQuery || mOcclusionQuery->getAllocatedVisibilityOffsets().empty())
+ if (mOcclusionQueryResultBuffer && mOcclusionQueryIsEnabledInRenderPass)
{
- return angle::Result::Continue;
+ ASSERT(mRenderEncoder.valid());
+ mRenderEncoder.setVisibilityResultMode(MTLVisibilityResultModeDisabled, 0);
+ mOcclusionQueryIsEnabledInRenderPass = false;
}
-
- return startOcclusionQueryInRenderPass(mOcclusionQuery, false);
}
-angle::Result ContextMtl::startOcclusionQueryInRenderPass(QueryMtl *query, bool clearOldValue)
+angle::Result ContextMtl::enableOcclusionQueryInRenderPass()
{
ASSERT(mRenderEncoder.valid());
-