[Bug 60257] New: win32u: send_mouse_motion() skips resetting raw_mouse.count on empty input, causing a heap buffer overflow
http://bugs.winehq.org/show_bug.cgi?id=60257 Bug ID: 60257 Summary: win32u: send_mouse_motion() skips resetting raw_mouse.count on empty input, causing a heap buffer overflow Product: Wine Version: 11.13 Hardware: x86-64 OS: Linux Status: UNCONFIRMED Severity: normal Priority: P2 Component: win32u Assignee: wine-bugs@list.winehq.org Reporter: GenShinigami@gmail.com Target Milestone: --- Distribution: --- Please note: I am not someone with the tech-knowledge to have done this alone, and want to be transparent that I have been using a LLM to assist in investigating and resolving it. I've tested the fix it has provided, and it has been working for me. Apologies for not providing something that I personally dug into and identified, and if providing LLM-generated finds is not allowed, I also apologize of that. This particular bug was found while using the Shadowrun 5e character creation app called "Chummer5a" wihch is a .NET character generator that I am running through WINE. accum_mouse_motion() in dlls/win32u/input.c accumulates raw mouse motion frames into a fixed 64-entry buffer (struct raw_mouse { UINT count; POINT data[64]; }, embedded in struct user_thread_info). When the buffer is about to overflow, it calls send_mouse_motion() to flush the accumulated input and reset the counter. send_mouse_motion() has an early-return optimization to skip sending genuinely empty input: static NTSTATUS send_mouse_motion( UINT flags ) { struct user_thread_info *info = get_user_thread_info(); INPUT input = info->mouse_motion; NTSTATUS status; if (!input.mi.dwFlags && !input.mi.mouseData) return STATUS_SUCCESS; /* ignore empty inputs */ TRACE( "Sending %s (%u raw frames)\n", debugstr_mouseinput( &input.mi ), info->raw_mouse.count ); status = server_send_hardware_message( info->mouse_hwnd, flags, &input, (LPARAM)&info->raw_mouse ); memset( &info->mouse_motion, 0, sizeof(info->mouse_motion) ); info->raw_mouse.count = 0; info->mouse_hwnd = NULL; return status; } The early return happens BEFORE info->raw_mouse.count = 0. If info->mouse_motion happens to be empty (dwFlags == 0 && mouseData == 0) at the exact moment accum_mouse_motion() calls this to flush an about-to-overflow buffer, the function returns without resetting the counter. The caller has no way to know the reset didn't happen, and proceeds to memcpy into the buffer and increment the counter anyway - now starting from a value that's already at (or past) capacity. Every subsequent raw mouse frame writes one slot further out of bounds. In our reproduction the counter reached 73 (9 slots, i.e. 72 bytes, past the 64-entry/512-byte buffer) before the overflow reached and corrupted session_data, a pointer field later in the same struct user_thread_info that's dereferenced on essentially every call into the shared message-queue code (get_shared_queue() and friends in dlls/win32u/winstation.c). Once that pointer is corrupted, the affected thread SIGSEGVs on every subsequent call to those functions, recovers, and retries - producing a CPU-pinned livelock rather than a clean crash, since Wine's own signal handler catches and recovers from the fault each time. Reproduction: we were not able to isolate a minimal, deterministic repro - it depends on the exact timing/sequence of real mouse input (specifically, a run of raw motion frames with no accompanying regular move/button/wheel data landing right as the 64-entry accumulator is about to overflow). In our case it surfaced as an intermittent CPU-pinning hang in a .NET application under Wine, recurring anywhere from under a minute to about ten minutes into a session, confirmed via a hardware watchpoint on the corrupted field across multiple independent captures (all showing the same raw_mouse.count pattern: 9 past capacity). Suggested fix: perform the accumulator reset (mouse_motion, raw_mouse.count, mouse_hwnd) unconditionally on every flush; only the actual send to wineserver should be skipped for empty input: static NTSTATUS send_mouse_motion( UINT flags ) { struct user_thread_info *info = get_user_thread_info(); INPUT input = info->mouse_motion; NTSTATUS status = STATUS_SUCCESS; if (input.mi.dwFlags || input.mi.mouseData) { TRACE( "Sending %s (%u raw frames)\n", debugstr_mouseinput( &input.mi ), info->raw_mouse.count ); status = server_send_hardware_message( info->mouse_hwnd, flags, &input, (LPARAM)&info->raw_mouse ); } memset( &info->mouse_motion, 0, sizeof(info->mouse_motion) ); info->raw_mouse.count = 0; info->mouse_hwnd = NULL; return status; } This preserves the original "don't bother telling wineserver about an empty input" optimization while guaranteeing the accumulator's own bookkeeping is always reset on a flush, closing the overflow path. We've built and run this fix locally (wine-staging 11.13) and confirmed it resolves the livelock - one hour of continuous use with zero recurrences, versus recurring every few minutes before the fix. -- Do not reply to this email, post in Bugzilla using the above URL to reply. You are receiving this mail because: You are watching all bug changes.
http://bugs.winehq.org/show_bug.cgi?id=60257 Rémi Bernon <rbernon@codeweavers.com> changed: What |Removed |Added ---------------------------------------------------------------------------- Resolution|--- |FIXED CC| |rbernon@codeweavers.com Status|UNCONFIRMED |RESOLVED Fixed by SHA1| |a1bae27f21b53fa3ac6be6b08f4 | |cbfb441360ade --- Comment #1 from Rémi Bernon <rbernon@codeweavers.com> --- Thanks for the report but I believe this has been fixed already in a1bae27f21b53fa3ac6be6b08f4cbfb441360ade (in wine-11.15). -- Do not reply to this email, post in Bugzilla using the above URL to reply. You are receiving this mail because: You are watching all bug changes.
http://bugs.winehq.org/show_bug.cgi?id=60257 Alexandre Julliard <julliard@winehq.org> changed: What |Removed |Added ---------------------------------------------------------------------------- Status|RESOLVED |CLOSED --- Comment #2 from Alexandre Julliard <julliard@winehq.org> --- Closing bugs fixed in 11.17. -- Do not reply to this email, post in Bugzilla using the above URL to reply. You are receiving this mail because: You are watching all bug changes.
participants (1)
-
WineHQ Bugzilla