[7] GameControllerGamepad UAF from raw this capture in framework-retained blocks
Severity: Medium | Component: WebCore Gamepad | 101c855
Rated Medium because the observable effect is a confirmed use-after-free crash on controller disconnect (the test case reproduces it), but exploitation requires physical or virtual HID device access — not triggerable from web content alone — and the race window is narrow, limiting reliable exploitation despite the UI-process context.
Replaces raw this captures in Objective-C callback blocks with WeakPtr plus null checks. Adds a destructor that calls teardownElements() to unregister all GameController framework handlers when the object is destroyed.
Source/WebCore/platform/gamepad/cocoa/GameControllerGamepad.mm
void GameControllerGamepad::setupElements()
{
RetainPtr<GCPhysicalInputProfile> profile = m_gcController.get().physicalInputProfile;
+ WeakPtr weakThis { *this };
if ([profile respondsToSelector:@selector(setThumbstickUserIntentHandler:)]) {
[profile setThumbstickUserIntentHandler:^(__kindof GCPhysicalInputProfile*, GCControllerElement*) {
- m_lastUpdateTime = MonotonicTime::now();
- GameControllerGamepadProvider::singleton().gamepadHadInput(*this, true);
+ if (!weakThis)
+ return;
+ weakThis->m_lastUpdateTime = MonotonicTime::now();
+ GameControllerGamepadProvider::singleton().gamepadHadInput(*weakThis, true);
}];
}
+GameControllerGamepad::~GameControllerGamepad()
+{
+ teardownElements();
+}
+void GameControllerGamepad::teardownElements()
+{
+ auto profile = RetainPtr { m_gcController.get().physicalInputProfile };
+ if (!profile)
+ return;
+
+ if ([profile respondsToSelector:@selector(setThumbstickUserIntentHandler:)])
+ [profile setThumbstickUserIntentHandler:nil];
+
+ for (GCControllerButtonInput *button in [profile allButtons])
+ button.valueChangedHandler = nil;
+
+ profile.get().dpads[GCInputLeftThumbstick].xAxis.valueChangedHandler = nil;
+ profile.get().dpads[GCInputLeftThumbstick].yAxis.valueChangedHandler = nil;
+ profile.get().dpads[GCInputRightThumbstick].xAxis.valueChangedHandler = nil;
+ profile.get().dpads[GCInputRightThumbstick].yAxis.valueChangedHandler = nil;
+}
Tools/TestWebKitAPI/Tests/mac/HIDGamepads.mm
+TEST(Gamepad, DisconnectDuringInput)
+{
+ // Tests that handler blocks don't crash when invoked after GameControllerGamepad destruction.
+ ...
+ for (int i = 0; i < 100; ++i) {
+ gamepad->setAxisValue(0, (float)i / 100.0);
+ gamepad->setAxisValue(1, (float)(100 - i) / 100.0);
+ gamepad->publishReport();
+ }
+
+ // This destroys VirtualGamepad, which triggers controllerDidDisconnect,
+ // which destroys GameControllerGamepad.
+ gamepad = nullptr;
+
+ // Wait for disconnect message.
+ Util::run(&didReceiveMessage);
+}
Patch Details
The patch makes three changes: (1) setupElements() captures a WeakPtr weakThis { *this } and all callback blocks now check if (!weakThis) return; before accessing member variables. (2) A new ~GameControllerGamepad() destructor calls teardownElements(). (3) A new teardownElements() method sets all registered handler blocks to nil on the GCPhysicalInputProfile, preventing the framework from firing callbacks after destruction. The fix applies a belt-and-suspenders approach — both WeakPtr null checks (defense in depth) and handler deregistration (primary fix). Notably, the axis handler blocks still pass *this instead of *weakThis to gamepadHadInput() — an apparent oversight, though teardownElements() mitigates this by clearing handlers on destruction.
Background
The Gamepad API exposes physical game controllers to web content. On Apple platforms, WebKit uses the GameController framework (GCController, GCPhysicalInputProfile) to receive input events. Input handlers are registered as Objective-C blocks on controller elements (buttons, axes, thumbsticks). These blocks are retained by the framework and invoked asynchronously when the hardware reports new input — their lifetime is controlled by the Objective-C runtime, not by the C++ object that registered them.
WeakPtr is a weak reference that automatically nullifies when the pointee is destroyed, allowing safe liveness checks in retained callbacks. Without explicit cleanup, Objective-C blocks captured by framework objects will persist indefinitely, holding references to whatever they closed over.
Analysis
Use-after-free from raw this capture in framework-retained callback blocks that outlive the C++ object.
Before the fix, GameControllerGamepad::setupElements() registered Objective-C block callbacks with the GameController framework that captured the raw this pointer. These blocks were retained by the GCController's input profile and could fire asynchronously at any time when the physical controller produced input. When a GameControllerGamepad object was destroyed (e.g., on controller disconnect), no destructor existed to unregister these handlers. The GameController framework continued to hold the blocks, and when a queued or new input event fired the callback, the block dereferenced the now-freed this pointer, writing to m_axisValues, m_buttonValues, and m_lastUpdateTime, and passing a dangling reference to gamepadHadInput().
Aaa Aaaa Aaaa Aaaaaaaa Aaa Aaaa Aaaaaaa Aaaaa Aaaaaaaaaaaaaaaaa Aaaaa Aaaaaaaa Aa Aaaaaaaaa Aaaaaaaaaaa Aaa Aaaaaaaa a Aaaaaaaaa Aaa Aaaa Aaaaaaa Aaaaa Aa Aaaaaaaaaaa Aaaaaa Aaaaa Aaaa Aa a Aaaa Aaaaaaaaaaa Aaaaaaaaaa Aaa Aaaaaa Aaaaaaaaaaa
Aa Aaaaaaaa Aaaa Aaa Aaaaaaa Aa Aaaaaaa a Aaaaaaa Aaa Aaaaaaa Aaaaaa Aaaa Aaa Aa Aaaaaaaaaa Aaaaaa Aaa Aaaaaaa Aaa Aaaaaaa Aaaaaaa Aa Aaaaaa Aaaaaaa a Aaaaaaaaaaaaaaaaaaaaaaaa Aaa Aaaaaaa Aaaa Aaaaa Aaaaaaa Aa Aaaaa Aaaaaaaa Aaaaaaaaaaaa Aaa Aaaaaaaaaa Aaa Aaaaaa Aa Aaaaaaa Aaaaaaaaaaaa Aa Aaa Aaaaaa Aaaaaaaaa Aaaa Aaaaa Aaaaaaaaaaaa Aaa Aaaaa Aaaaaa Aaaaaaa Aaaaaaaaaaaa Aaaa Aaaaa Aaaa Aaaaaaa Aaa Aaaaaaaaa Aaaaa Aaaaaaaaaaaaaaaaaaa Aaaaa Aaaaaa Aaaaaaaa Aaaaaaaaa Aaaa Aaa Aaa Aaaaaaa Aa Aaaaa Aaaaaaa Aaaa Aaa Aaaaaaaa Aaaaaa Aaaaaaa Aaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaa Aa Aaa Aaaaa Aaaaaaaaaa Aa Aaaaaaaaa Aaaa Aaaaaaaaaaaaaaaaaaa Aaaaaaaa Aaaaa Aaaaaa Aaaa Aa Aaa Aaaaaaaaaaa Aaaaaaa
Aaaaaaaaaaaaaa Aaaaaaaaaaa Aaaa Aa Aaa Aa Aaaaaaa Aa Aaaaa Aaaaaaaaa Aaaa Aaa Aaaaaaaa Aaaaaaaaaaaaaa Aaa Aaa Aaaaaaaaa Aaaaaaaaaa Aaaaaaa a Aaaaaa Aaaaaaaaaa Aaaaaaaaaaaa Aaaaaaaaaaa Aaa Aaaaaaaaa Aaaaaaaa Aa Aaaaaaa Aaa Aaaaaa Aaaaaaa Aaaaa Aaaaaaaaaaaaa Aaaaaa Aaaaaa Aaaaaaaaaaaaaaa
Aaaa Aaaaaaaaaaaaa Aaaaaaa Aaaaaa Aaaaaa Aa Aaa Aaaaaaa Aaaaaaa Aaaaaaaaaaaaaa Aaaaaaaaaaaa Aaa Aaaaaaa Aaaa Aaaaaa a Aaaa Aaaaaaaaa Aaaaa Aaaaaaaaaa Aaaaaaaaaa Aaa Aaaaaaaaaa Aaaaa Aaaaaa Aaaaaaa a Aaaaaaaaaaaaaaaa Aaaaaaaaaaaa
🔒Explores the heap exploitation potential of the dangling-pointer writes and the process-level implications of where this code executes
Subscribe to read more
Audit directions
a Aaaaaaaa Aaa Aaaaaaa Aaaaaaaaaaa Aaaaaaaaaaa Aaaaaa Aaaa Aaaaaaa Aaaaaa Aaaa Aaaaaaaaaaaaaaa Aaaaaaa Aaaaaaaaaaaaaaaa Aaaaaaaaaaaaaa Aaaaaaaaaaaaa Aaaaaaaaaaa Aa Aaa Aaa Aaaaaaaaaa Aaaa Aaa Aaaaaaaaaa Aaa Aaaaaaaa Aaa Aaaaa Aaaaaaaa Aaa Aaaaaaa Aaaa Aaa Aaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaa Aa Aaaaa Aaaaaaaaaaa Aa Aaaaa Aaaaa Aaaa Aaaaaaa Aaaaaa Aa Aaaaaa Aaaaaaaaa Aaaaaaaaa Aaaaa Aaaa Aaaaaaaaaaaaaaaaaaaaaaaaaa a Aaaaaaaaaaaa Aaaaaaaaaaa Aaaaaaaaaaaaaaa Aaa Aaaaaaaaaaaaaaa
a Aaaaaaaa Aaaaaaaaaa Aaaaaaa Aaaaaaaaa Aaaaa Aaaa Aaaaaaaa Aaaaaa Aa Aaa Aaaa Aaaaaa Aaa Aaaaaaa Aaa Aaaaaa Aaaaaa Aaa Aaaaaa Aaaaaaaaa Aa Aaaa Aaaa Aaaaaaa Aaa Aaaa Aaaa Aaaaaaaaaaaaaaaaaaaaa Aaaaaa Aaa Aaaaaaaaaa Aaa Aaaaaa Aaaaaa Aaa Aaaaa Aaaa Aaaaaaa Aaaa Aaaaaaaaaaaa Aa Aaaaaaaaaaaaaaaaaaaa Aaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaa Aaaa Aaaaaaaa Aaa Aaaaaaaa Aaa Aaaa Aaaaa Aaaaa Aaaaa Aaa Aaaaa Aaaaaaaaaaaaaaaaa Aaaaa Aaaaaa Aaa Aaaa Aaaaaaa
a Aaaaaaaa Aaaaaaa Aaaaaaaaaa Aaaaaaa Aa Aaaaaaaa Aaaaaaaaaaa Aaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaa Aaa Aa Aaaaaaaaaa Aaaaaa Aaaa Aaaa Aaaaa Aaaaa Aaaaaaaaaaaaaaaaa Aaaaaaaaaa Aaa Aaaaaaa Aaaaaaaa Aaaaaa Aaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaa Aaaaaaa Aaaaaaaaaaa Aaaa Aaaaaa Aaaaaaaaaa Aaaaaaaaa Aaaaaaaaaa Aaaaa Aaaaaaaaaaaaa Aaaaaaaaaaaaa Aaa Aaa Aaaaa Aaaa Aaaaaa a Aaaaaaaaaaa Aa a Aaaaaaaaa Aaaaaa Aa Aaaaa Aa Aaaaaaaaa Aaaaaaaaa
🔒Multiple audit patterns identified for framework callback lifetime issues across WebKit's platform integration layers, with concrete search targets
Subscribe to read more