CVE-2026-9972
Overview
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifdevice/gamepad/gamepad_device_mac.mm |
modified |
Files Changed
device/gamepad/gamepad_device_mac.hdevice/gamepad/gamepad_device_mac.mm
Patch
From b90a779912796e317ee82c393a4c5cfcd3402347 Mon Sep 17 00:00:00 2001
From: Rob Pitkin <robpitkin@google.com>
Date: Fri, 01 May 2026 18:40:59 -0700
Subject: [PATCH] gamepad: Fix uninitialized memory access in GamepadDeviceMac
In GamepadDeviceMac::SetVibration, the code checked ff_device_ref_
to determine if it should proceed with setting vibration. However, if
CreateForceFeedbackEffect fails (e.g., because the device does not
support custom forces), ff_effect_ref_ will be null, but
ff_device_ref_ might still be non-null.
This leads to accessing ff_effect_.lpvTypeSpecificParams which was
never initialized, containing garbage data from the heap. This could
result in an arbitrary memory write.
This CL fixes the issue by:
1. Checking ff_effect_ref_ instead of ff_device_ref_ in SetVibration.
2. Value-initializing ff_effect_ and ff_custom_force_ in the header
to ensure they are zeroed out if not explicitly set.
Bug: 508463705
Change-Id: I17b69b804a1bfd58257412ef71e5d92b2ed268b8
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7807948
Commit-Queue: Rob Pitkin <robpitkin@chromium.org>
Reviewed-by: Matt Reynolds <mattreynolds@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1624215}
---
diff --git a/device/gamepad/gamepad_device_mac.h b/device/gamepad/gamepad_device_mac.h
index 540d14e..3d0998b 100644
--- a/device/gamepad/gamepad_device_mac.h
+++ b/device/gamepad/gamepad_device_mac.h
@@ -110,8 +110,8 @@
// Force feedback
FFDeviceObjectReference ff_device_ref_;
FFEffectObjectReference ff_effect_ref_;
- FFEFFECT ff_effect_;
- FFCUSTOMFORCE ff_custom_force_;
+ FFEFFECT ff_effect_{};
+ FFCUSTOMFORCE ff_custom_force_{};
LONG force_data_[2];
DWORD axes_data_[2];
LONG direction_data_[2];
diff --git a/device/gamepad/gamepad_device_mac.mm b/device/gamepad/gamepad_device_mac.mm
index 0731eb00..ca8176f0 100644
--- a/device/gamepad/gamepad_device_mac.mm
+++ b/device/gamepad/gamepad_device_mac.mm
@@ -471,7 +471,7 @@
return;
}
- if (ff_device_ref_) {
+ if (ff_effect_ref_) {
FFCUSTOMFORCE* ff_custom_force =
static_cast<FFCUSTOMFORCE*>(ff_effect_.lpvTypeSpecificParams);
DCHECK(ff_custom_force);
Original Bug Report
Potential arbitrary write in Browser via uninitialized memory in GamepadDeviceMac
Flapjack, an experimental security project, has identified the following potential security issue. If you’re a feature owner CC-ed on this bug, please do your best to review these reports without the Chrome Security team. Please see https://chromium.googlesource.com/chromium/src/+/main/docs/security/ai-generated-security-bugs-faq.md for more information.
Overview: A potential arbitrary write vulnerability exists in the macOS gamepad implementation (GamepadDeviceMac). If a connected gamepad lacks custom force capabilities, a POD struct remains uninitialized on the heap. A compromised renderer can leverage this uninitialized memory via the Gamepad API to write controlled data to arbitrary addresses in the Browser process.
Affected files:
device/gamepad/gamepad_device_mac.mmdevice/gamepad/gamepad_device_mac.h
Estimated timestamp from git blame: 2018-01-12
Summary
A logic error and uninitialized memory vulnerability in GamepadDeviceMac allows a compromised renderer process to potentially achieve an arbitrary memory write in the Browser process on macOS. This issue occurs when handling generic gamepads that support Force Feedback but lack the FFCAP_ET_CUSTOMFORCE capability, leading to the use of an uninitialized system struct containing raw pointers.
Technical Details
The GamepadDeviceMac class (device/gamepad/gamepad_device_mac.mm) contains a member ff_effect_ of type FFEFFECT, which is a POD C-struct defined in the macOS ForceFeedback framework. Because this member is omitted from the constructor’s initializer list, C++ default-initialization rules leave its memory contents indeterminate when the object is allocated on the heap.
During object construction, the code attempts to initialize this struct by calling CreateForceFeedbackEffect. However, if the device lacks the FFCAP_ET_CUSTOMFORCE capability, the function returns early without touching the struct:
// In GamepadDeviceMac::CreateForceFeedbackEffect
if ((caps.supportedEffects & FFCAP_ET_CUSTOMFORCE) == 0)
return nullptr;
// ... struct initialization happens after this point ...
When a website requests a vibration effect, GamepadDeviceMac::SetVibration is called. This function contains a critical logic error: it checks whether the device reference (ff_device_ref_) is valid, rather than checking if the effect reference (ff_effect_ref_) was successfully created.
// In GamepadDeviceMac::SetVibration
if (ff_device_ref_) { // BUG: Should check ff_effect_ref_
FFCUSTOMFORCE* ff_custom_force =
static_cast<FFCUSTOMFORCE*>(ff_effect_.lpvTypeSpecificParams);
DCHECK(ff_custom_force); // Compiled out in release builds
DCHECK(ff_custom_force->rglForceData);
ff_custom_force->rglForceData[0] =
static_cast<LONG>(params->strong_magnitude * kRumbleMagnitudeMax);
ff_custom_force->rglForceData[1] =
static_cast<LONG>(params->weak_magnitude * kRumbleMagnitudeMax);
Because ff_device_ref_ is non-null but ff_effect_ is uninitialized, the code treats indeterminate heap data as the lpvTypeSpecificParams pointer. It then performs a double-indirection read to resolve rglForceData, and writes the attacker-provided strong_magnitude and weak_magnitude values (scaled by kRumbleMagnitudeMax) to that address.
MiraclePtr (BackupRefPtr) does not mitigate this because FFEFFECT is a macOS system struct using raw C pointers.
Suggested Exploitation Steps
Note: These are potential steps based on code analysis; our tooling cannot run live code to provide a working PoC.
- Heap Spray: A compromised renderer process uses Mojo IPC to spray the Browser process’s heap, populating unallocated memory chunks with a crafted payload. This payload sets up a fake pointer chain for
lpvTypeSpecificParamsandrglForceDatatargeting a critical memory address (e.g., a vtable or callback pointer). - Trigger Allocation: A generic gamepad (supporting basic force feedback but not custom forces) is connected. The Browser allocates a
GamepadDeviceMacover the sprayed heap memory. - Uninitialized Memory: The
ff_effect_struct inherits the attacker’s fake pointer chain. - Arbitrary Write: The compromised renderer sends a
PlayVibrationEffectOnceMojo message, bypassing standard JS clamping to provide specificdoublemagnitude values. - Execution: The Browser process processes the vibration, dereferences the attacker-controlled pointer chain, and writes the calculated
LONG(64-bit on macOS) values to the attacker’s chosen address, potentially allowing a full sandbox escape.
Suggested Fix
- Initialize POD members: In
device/gamepad/gamepad_device_mac.h, explicitly value-initialize the POD structs by appending{}:FFEFFECT ff_effect_{}; FFCUSTOMFORCE ff_custom_force_{}; - Fix the logic check: In
device/gamepad/gamepad_device_mac.mm, updateSetVibrationto check the effect reference rather than the device reference:// Change line 474 from: // if (ff_device_ref_) { // To: if (ff_effect_ref_) {
Evaluated with Chrome root at commit: cc901875d53bf4e4fe0e01f02843871da4106e70
Results so far have been promising, but there can be wrong deductions. If this proves to be a false positive, please close as WAI; data from false positives will be used to improve accuracy over time. And please feel free to reach out to me directly if you have concerns or feedback on the project.