* [PATCH] HID: magicmouse: fix battery reporting for Bluetooth Magic Trackpad USB-C
@ 2026-07-06 17:55 Andrei Fed
2026-07-06 18:13 ` sashiko-bot
0 siblings, 1 reply; 2+ messages in thread
From: Andrei Fed @ 2026-07-06 17:55 UTC (permalink / raw)
To: Jiri Kosina, Benjamin Tissoires, linux-input
Cc: linux-kernel, Andrei Fed, stable
The Apple Magic Trackpad 2 (USB-C) reports a wildly wrong battery
capacity over Bluetooth, for example a constant 4% for a pack that is
actually at 74%.
The device's battery input report (0x90) is laid out as
[report-id][status][charge]. hid-input's synchronous capacity query,
hidinput_query_battery_capacity(), assumes the common
[report-id][capacity] layout and returns buf[1], which for this device
is the status byte rather than the charge (buf[2]).
magicmouse_fetch_battery(), which requests the battery report through
hid_hw_request() so the reply is decoded via the report descriptor at
the correct field offset, is gated to the USB models and never runs
over Bluetooth. The device does not push battery reports on its own
either, except a single one at connect time, which is delivered while
probe holds driver_input_lock and is silently dropped. All userspace
reads therefore go through the misparsing query, and the device is
stuck reporting its status byte as the capacity.
Enabling the fetch for Bluetooth is not sufficient on its own: user
space reacts to the power_supply registration immediately, so a query
is typically already in flight when the fetch reply is parsed.
hidinput_get_battery_property() stores the query result and marks the
battery as queried without rechecking whether a report arrived while
it was waiting, clobbering the just-reported correct value with the
misparsed one.
Fix this by adding HID_BATTERY_QUIRK_AVOID_QUERY for the Bluetooth
Magic Trackpad USB-C so the misparsing query path is never used, and
by fetching the battery at the end of probe for this device. hidp has
no asynchronous request() callback, so the fetch is serviced
synchronously via __hid_request() while probe still holds
driver_input_lock; call hid_device_io_start() first so the reply is
processed instead of being discarded.
Tested with a Magic Trackpad USB-C (004c:0324) over Bluetooth on
6.18.37: the reported capacity now matches the device (verified against
a raw GET_REPORT of report 0x90) and updates on reconnect.
Fixes: 87a2f10395c8 ("HID: magicmouse: Apple Magic Trackpad 2 USB-C driver support")
Cc: stable@vger.kernel.org
Signed-off-by: Andrei Fed <andfed.net@gmail.com>
---
drivers/hid/hid-input.c | 3 +++
drivers/hid/hid-magicmouse.c | 19 ++++++++++++++++++-
2 files changed, 21 insertions(+), 1 deletion(-)
diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c
index 3487600..f78166f 100644
--- a/drivers/hid/hid-input.c
+++ b/drivers/hid/hid-input.c
@@ -375,6 +375,9 @@ static const struct hid_device_id hid_battery_quirks[] = {
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE,
USB_DEVICE_ID_APPLE_MAGICTRACKPAD),
HID_BATTERY_QUIRK_IGNORE },
+ { HID_BLUETOOTH_DEVICE(BT_VENDOR_ID_APPLE,
+ USB_DEVICE_ID_APPLE_MAGICTRACKPAD2_USBC),
+ HID_BATTERY_QUIRK_AVOID_QUERY },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ELECOM,
USB_DEVICE_ID_ELECOM_BM084),
HID_BATTERY_QUIRK_IGNORE },
diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c
index 802a347..0c4e959 100644
--- a/drivers/hid/hid-magicmouse.c
+++ b/drivers/hid/hid-magicmouse.c
@@ -828,6 +828,12 @@ static bool is_usb_magictrackpad2(__u32 vendor, __u32 product)
product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2_USBC;
}
+static bool is_bt_magictrackpad2(__u32 vendor, __u32 product)
+{
+ return vendor == BT_VENDOR_ID_APPLE &&
+ product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2_USBC;
+}
+
static int magicmouse_fetch_battery(struct hid_device *hdev)
{
#ifdef CONFIG_HID_BATTERY_STRENGTH
@@ -838,7 +844,8 @@ static int magicmouse_fetch_battery(struct hid_device *hdev)
bat = hid_get_battery(hdev);
if (!bat ||
(!is_usb_magicmouse2(hdev->vendor, hdev->product) &&
- !is_usb_magictrackpad2(hdev->vendor, hdev->product)))
+ !is_usb_magictrackpad2(hdev->vendor, hdev->product) &&
+ !is_bt_magictrackpad2(hdev->vendor, hdev->product)))
return -1;
report_enum = &hdev->report_enum[bat->report_type];
@@ -971,6 +978,16 @@ static int magicmouse_probe(struct hid_device *hdev,
schedule_delayed_work(&msc->work, msecs_to_jiffies(500));
}
+ /*
+ * Query the Bluetooth Magic Trackpad USB-C battery as done for USB.
+ * Start io first: probe holds driver_input_lock and the synchronous
+ * GET_REPORT reply would otherwise be dropped.
+ */
+ if (is_bt_magictrackpad2(id->vendor, id->product)) {
+ hid_device_io_start(hdev);
+ magicmouse_fetch_battery(hdev);
+ }
+
return 0;
err_stop_hw:
if (is_usb_magicmouse2(id->vendor, id->product) ||
--
2.54.0
^ permalink raw reply related [flat|nested] 2+ messages in thread* Re: [PATCH] HID: magicmouse: fix battery reporting for Bluetooth Magic Trackpad USB-C
2026-07-06 17:55 [PATCH] HID: magicmouse: fix battery reporting for Bluetooth Magic Trackpad USB-C Andrei Fed
@ 2026-07-06 18:13 ` sashiko-bot
0 siblings, 0 replies; 2+ messages in thread
From: sashiko-bot @ 2026-07-06 18:13 UTC (permalink / raw)
To: Andrei Fed; +Cc: dmitry.torokhov, linux-input
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [Critical] Unbounded recursion in `magicmouse_raw_event` when parsing malicious `DOUBLE_REPORT_ID` packets can cause a kernel stack overflow.
--
commit 58987e236bab550dc05b66346833ee21ed261110
Author: Andrei Fed <andfed.net@gmail.com>
HID: magicmouse: fix battery reporting for Bluetooth Magic Trackpad USB-C
This commit fixes battery reporting for the Bluetooth Magic Trackpad USB-C by
avoiding the misparsing query path. It introduces a battery quirk to skip the
query and instead fetches the battery at the end of the device probe.
> diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c
> index 802a3479e24b9..0c4e959322c3c 100644
> --- a/drivers/hid/hid-magicmouse.c
> +++ b/drivers/hid/hid-magicmouse.c
[ ... ]
[Severity: Critical]
This is a pre-existing issue, but while reviewing this file I noticed
that magicmouse_raw_event() doesn't seem to enforce a depth limit when
recursively calling itself to parse DOUBLE_REPORT_ID packets:
drivers/hid/hid-magicmouse.c:magicmouse_raw_event() {
...
case DOUBLE_REPORT_ID:
/* Sometimes the trackpad sends two touch reports in one
* packet.
*/
/* Ensure that we have at least 2 elements (report type and size) */
if (size < 2)
return 0;
if (size < data[1] + 2) {
hid_warn(hdev,
"received report length (%d) was smaller than specified (%d)",
size, data[1] + 2);
return 0;
}
magicmouse_raw_event(hdev, report, data + 2, data[1]);
magicmouse_raw_event(hdev, report, data + 2 + data[1],
size - 2 - data[1]);
return 0;
...
}
If a malicious Bluetooth or USB device sends a HID report starting with
DOUBLE_REPORT_ID (0xf7) and filled with the sequence [0xf7, 0x00], the inner
size byte data[1] evaluates to 0.
Could this cause magicmouse_raw_event() to recursively call itself with
size 0 (which returns immediately) and size - 2?
A 16KB malicious report could force ~8192 nested recursions, which might
overflow the standard kernel stack and lead to a denial of service panic.
Does this need an upper bound on recursion depth or an iterative approach
to prevent stack exhaustion?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260706175507.47288-1-andfed.net@gmail.com?part=1
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-07-06 18:13 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-06 17:55 [PATCH] HID: magicmouse: fix battery reporting for Bluetooth Magic Trackpad USB-C Andrei Fed
2026-07-06 18:13 ` sashiko-bot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox