CVE-2025-43511
Overview
Impact:
Description: Processing maliciously crafted web content may lead to an unexpected process crash
Researchers: 이동하 (Lee Dong Ha of BoB 14th)
Key insight. The inspector animation table stored animations by raw pointer (and dereferenced a possibly-null script-execution context), so a destroyed animation left a dangling entry; switching to WeakRef and null-checking the context restores lifetime safety.
Patch Details
diff --git a/Source/WebCore/inspector/agents/InspectorAnimationAgent.cpp b/Source/WebCore/inspector/agents/InspectorAnimationAgent.cpp
index 299fa548e71e..1399bcebf310 100644
--- a/Source/WebCore/inspector/agents/InspectorAnimationAgent.cpp
+++ b/Source/WebCore/inspector/agents/InspectorAnimationAgent.cpp
@@ -362,7 +362,11 @@ Inspector::Protocol::ErrorStringOr<Ref<Inspector::Protocol::Runtime::RemoteObjec
if (!animation)
return makeUnexpected(errorString);
- auto* state = animation->protectedScriptExecutionContext()->globalObject();
+ RefPtr scriptExecutionContext = animation->scriptExecutionContext();
+ if (!scriptExecutionContext)
+ return makeUnexpected("Animation is detached from context"_s);
+
+ auto* state = scriptExecutionContext->globalObject();
auto injectedScript = m_injectedScriptManager.injectedScriptFor(state);
ASSERT(!injectedScript.hasNoValue());
@@ -588,7 +592,7 @@ void InspectorAnimationAgent::frameNavigated(LocalFrame& frame)
String InspectorAnimationAgent::findAnimationId(WebAnimation& animation)
{
for (auto& [animationId, existingAnimation] : m_animationIdMap) {
- if (existingAnimation == &animation)
+ if (existingAnimation.ptr() == &animation)
return animationId;
}
return nullString();
@@ -605,7 +609,7 @@ WebAnimation* InspectorAnimationAgent::assertAnimation(Inspector::Protocol::Erro
void InspectorAnimationAgent::bindAnimation(WebAnimation& animation, RefPtr<Inspector::Protocol::Console::StackTrace> backtrace)
{
auto animationId = makeString("animation:"_s, IdentifiersFactory::createIdentifier());
- m_animationIdMap.set(animationId, &animation);
+ m_animationIdMap.set(animationId, animation);
auto animationPayload = Inspector::Protocol::Animation::Animation::create()
.setAnimationId(animationId)
diff --git a/Source/WebCore/inspector/agents/InspectorAnimationAgent.h b/Source/WebCore/inspector/agents/InspectorAnimationAgent.h
index 1c5b538fe4d3..5eb7009f0189 100644
--- a/Source/WebCore/inspector/agents/InspectorAnimationAgent.h
+++ b/Source/WebCore/inspector/agents/InspectorAnimationAgent.h
@@ -100,7 +100,7 @@ private:
Inspector::InjectedScriptManager& m_injectedScriptManager;
WeakRef<Page> m_inspectedPage;
- MemoryCompactRobinHoodHashMap<Inspector::Protocol::Animation::AnimationId, WebAnimation*> m_animationIdMap;
+ HashMap<Inspector::Protocol::Animation::AnimationId, WeakRef<WebAnimation, WeakPtrImplWithEventTargetData>> m_animationIdMap;
WeakHashMap<WebAnimation, Ref<Inspector::Protocol::Console::StackTrace>, WeakPtrImplWithEventTargetData> m_animationsPendingBinding;
Timer m_animationBindingTimer;
Background
Advisory: Apple Security Advisory
| Attribute | Value |
|---|---|
| CVE | CVE-2025-43511 |
| Bugzilla | 300926 |
| Component | WebCore |
| Bug Class | UAF |
| Severity | medium |
| Commit | bb6619c2421179b0… |
| Advisory | Apple Advisory |
Terminology
- Web Inspector agent — Backend objects (here InspectorAnimationAgent) that service the inspector frontend’s protocol domains and hold references to inspected page objects.
- Raw pointer vs WeakRef — A raw T* is not lifetime-aware and dangles when the object dies; a WeakRef auto-nulls, so lookups can detect a dead object.
- WebAnimation / scriptExecutionContext — A Web Animations object bound to a document/worker context that can become detached (context null) during teardown or navigation.
- Detached animation — An animation whose scriptExecutionContext has gone away, so its global object is unavailable.
Root Cause Analysis
The Web Inspector animation agent tracked live animations in m_animationIdMap, a map from an inspector animation-id string to a raw WebAnimation*. Nothing kept those pointers in sync with object lifetime: when a WebAnimation was destroyed, its entry remained as a dangling raw pointer. Subsequent agent operations dereferenced it — findAnimationId compared each stored raw pointer to a live animation, and resolveAnimation did animation->protectedScriptExecutionContext()->globalObject() — yielding a use-after-free. resolveAnimation had a second defect: protectedScriptExecutionContext() can return null for an animation detached from its context, and the code dereferenced it unconditionally (a null/недействительный access). The fix changes m_animationIdMap to hold WeakRef<WebAnimation> values (which automatically drop when the animation dies, so lookups never see a stale pointer — findAnimationId now compares existingAnimation.ptr()), and bindAnimation stores the animation by weak reference. resolveAnimation now takes RefPtr scriptExecutionContext = animation->scriptExecutionContext() and returns an error (‘Animation is detached from context’) when it is null instead of dereferencing it. The restored invariant is that the inspector’s animation table observes animations weakly and validates liveness/context before use. This path is only reachable while a Web Inspector session is attached and driving the Animation domain.
Attack Path
1. Attach the inspector
An active Web Inspector session enables the Animation agent, which binds animations into m_animationIdMap by raw pointer.
2. Create and destroy an animation
Page script creates a Web Animation that the agent binds, then drops all references so the WebAnimation is collected, leaving a dangling raw pointer in the map.
3. Trigger a lookup
Invoke an agent operation (resolve/findAnimationId) that iterates or dereferences the stored pointer, or resolve an animation whose scriptExecutionContext is null.
4. Use-after-free / null deref
The agent dereferences the freed WebAnimation (or the null context), crashing or corrupting memory in WebContent.
Impact Assessment
A use-after-free (plus a null-context dereference) of a WebAnimation held by raw pointer in the inspector animation table, in the WebContent process. It is only reachable while a Web Inspector session is attached and exercising the Animation domain, which sharply limits real-world exploitability from a plain web page; the realistic outcome is a crash, with UAF-to-corruption possible under inspector-driven conditions. Rated medium.
Audit Directions
Other raw-pointer maps in inspector agents
grep Source/WebCore/inspector/agents for HashMap/Vector values typed as bare * (e.g. Node*, WebAnimation*) that outlive the pointee; prefer WeakRef/WeakHashMap.
Unchecked protected*() dereferences
grep for protectedScriptExecutionContext()-> and similar protected accessors dereferenced without a null check across WebCore.
Other id-to-object registries
Audit inspector domains (DOM, Canvas, Animation) that map protocol ids to engine objects for the same raw-pointer lifetime gap.
Changed Functions
| Function | File | Change | Note |
|---|---|---|---|
InspectorAnimationAgent::resolveAnimation |
Source/WebCore/inspector/agents/InspectorAnimationAgent.cpp |
modified | Takes scriptExecutionContext() into a RefPtr and returns an error when null instead of calling protectedScriptExecutionContext()->globalObject() unconditionally. |
InspectorAnimationAgent::findAnimationId |
Source/WebCore/inspector/agents/InspectorAnimationAgent.cpp |
modified | Compares existingAnimation.ptr() now that map values are WeakRef rather than raw pointers. |
InspectorAnimationAgent::bindAnimation |
Source/WebCore/inspector/agents/InspectorAnimationAgent.cpp |
modified | Stores the animation as a weak reference (m_animationIdMap.set(id, animation)) instead of &animation. |
m_animationIdMap |
Source/WebCore/inspector/agents/InspectorAnimationAgent.h |
modified | Changed from a map to WebAnimation* to a HashMap of WeakRef<WebAnimation, WeakPtrImplWithEventTargetData>, so entries auto-drop on destruction. |
Files Changed
Source Files
Source/WebCore/inspector/agents/InspectorAnimationAgent.cppSource/WebCore/inspector/agents/InspectorAnimationAgent.h