From 850d3b6376e83b4eeb61062e93c62dbf45a0bd71 Mon Sep 17 00:00:00 2001 From: Stefan Kerkmann Date: Tue, 1 Aug 2023 14:09:53 +0200 Subject: [PATCH] Fix mouse-key spamming empty reports Problem: `mousekey_task` spams empty hid reports with when a mouse key is pressed, causing resource exhaustion in the USB mouse endpoint. Cause: The check whether or not to send a new mouse report would always evaluate to true if a mouse key is pressed: 1. `mouse_report` has non-zero fields and `tmpmr` is a copy of this fields. 2. `mouse_report` is set to zero, `tmpmr` has now non-zero fields. 3. `has_mouse_report_changed` compares the two and evaluates to true 4. a mouse report is sent. Fix: The check condition of `has_mouse_report_changed` will evaluate any empty record as unchanged, as mouse report data is relative and doesn't need to return to zero. An empty report will still be send by `register_mouse` on release of all mouse buttons. --- tmk_core/protocol/report.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/tmk_core/protocol/report.c b/tmk_core/protocol/report.c index 5755098c60f1..1ba3be4604e0 100644 --- a/tmk_core/protocol/report.c +++ b/tmk_core/protocol/report.c @@ -281,13 +281,21 @@ void clear_keys_from_report(report_keyboard_t* keyboard_report) { #ifdef MOUSE_ENABLE /** - * @brief Compares 2 mouse reports for difference and returns result + * @brief Compares 2 mouse reports for difference and returns result. Empty + * reports always evaluate as unchanged. * * @param[in] new_report report_mouse_t * @param[in] old_report report_mouse_t * @return bool result */ __attribute__((weak)) bool has_mouse_report_changed(report_mouse_t* new_report, report_mouse_t* old_report) { - return memcmp(new_report, old_report, sizeof(report_mouse_t)); + // memcmp doesn't work here because of the `report_id` field when using + // shared mouse endpoint + bool changed = ((new_report->buttons != old_report->buttons) || +# ifdef MOUSE_EXTENDED_REPORT + (new_report->boot_x != 0 && new_report->boot_x != old_report->boot_x) || (new_report->boot_y != 0 && new_report->boot_y != old_report->boot_y) || +# endif + (new_report->x != 0 && new_report->x != old_report->x) || (new_report->y != 0 && new_report->y != old_report->y) || (new_report->h != 0 && new_report->h != old_report->h) || (new_report->v != 0 && new_report->v != old_report->v)); + return changed; } #endif