http://bugs.winehq.org/show_bug.cgi?id=60044 Bug ID: 60044 Summary: win32u: GUI thread self-deadlocks on font lock in NtGdiExtTextOutW -> NtGdiGetTextMetricsW Product: Wine Version: 11.13 Hardware: x86-64 OS: Linux Status: UNCONFIRMED Severity: normal Priority: P2 Component: gdi32 Assignee: wine-bugs@list.winehq.org Reporter: github@hotdogs.expert Target Milestone: --- Distribution: --- Created attachment 81503 --> http://bugs.winehq.org/attachment.cgi?id=81503 Backtraces, futex ownership, and objdump call-path analysis ## Product / Component - **Product:** Wine - **Component:** `gdi32` (font/text rendering; the code now lives in `win32u` but is triaged under gdi32 — a triager may move it) - **Version:** 11.13 - **Platform / OS:** x86-64 / Linux --- ## Description BeipMU (a Win32 MUD/MUSH client) becomes permanently unresponsive after several hours of normal use. The window keeps its last painted frame — title bar and menus are still drawn — but does not repaint or accept input. The process stays alive and uses almost no CPU. The GUI thread is deadlocked against **itself** on a static `pthread_mutex_t` in `win32u.so`'s `.bss`, reached through the text-drawing path while inside `NtUserDispatchMessage`. This is not lock contention. The mutex's `__owner` field contains the TID of the very thread that is blocked waiting for it, and no other thread in the process is anywhere near GDI code. ## Steps to reproduce 1. Run BeipMU under Wine with a `winex11.drv` (XWayland) session. 2. Leave it connected to a MUD so text is continuously rendered. 3. Wait. The freeze occurs after hours, at no reproducible point. **Reproducibility:** intermittent, hours-scale, no identified trigger. Observed twice. I have not found a minimal reproducer — I am filing on the strength of the backtrace rather than a recipe. ## Actual result Permanent GUI freeze; process alive, unkillable except by `wineserver -k`. ## Expected result No deadlock. --- ## Evidence ### Backtrace (`eu-stack -p <pid>`) ``` TID 2025: <- GUI / main thread #0 0x00007ff23a2a83ce #1 0x00007ff23a2af5b5 pthread_mutex_lock #2 0x00007ff2396ae81f win32u.so +0xae81f #3 0x00007ff2396c5498 NtGdiGetTextMetricsW #4 0x00007ff2396c6029 NtGdiExtTextOutW #5 0x00007ff23a167e7a ntdll (unix call thunk) #6 0x00007ff2396fe3f6 NtUserDispatchMessage #7 0x00007ff23a167e7a #8 0x00007ff23a165696 #9 0x00007ff23a4d805c #10 0x00007ff23a227c8e #11 0x00007ff23a227dcb __libc_start_main #12 0x00007ff23a4d8105 _start TID 3953 / 6167: ioctl -> NtWaitForMultipleObjects (idle) TID 10489: syscall -> NtWaitForAlertByThreadId (idle) ``` The three non-GUI threads are idle and hold nothing. ### Symbol attribution is verified, not guessed Return addresses fall *inside* the symbols' size ranges, so these are genuinely those functions and not nearest-preceding-symbol artefacts: | frame | address | symbol range | |---|---|---| | `NtGdiGetTextMetricsW` | `0xc5498` | `0xc5420`–`0xc56e9` | | `NtGdiExtTextOutW` | `0xc6029` | `0xc5eb0`–`0xc787f` | | `NtUserDispatchMessage` | `0xfe3f6` | `0xfe1b0`–`0xfe585` | ### The mutex, and proof of self-deadlock Read live from `/proc/<pid>/task/<tid>/syscall` and `/proc/<pid>/mem`: ``` syscall: 202 (futex) uaddr=0x7ff2398b1600 op=0x80 (FUTEX_WAIT_PRIVATE) val=0x2 __lock = 0x00000002 (locked, waiters present) __count = 0 (non-recursive mutex) __owner = 2025 <-- the blocked thread itself waiting thread = 2025 ``` `uaddr` resolves to `win32u.so + 0x2b1600`, inside `.bss` (`0x28bb80`–`0x2f96c8`), i.e. a statically-initialised file-local mutex. It is referenced from ~20 sites clustered in `0xab000`–`0xb6000`, consistent with a single global font lock. ### Static call path (from `objdump`) ``` NtGdiExtTextOutW c6023: call c5420 <NtGdiGetTextMetricsW> <- entry point calls entry point NtGdiGetTextMetricsW c5496: call *%rax <- physdev/driver dispatch <local fn at 0xae800> ae80a: lea ...(%rip),%rdi # 2b1600 <- loads the font mutex ae819: call *...(%rip) <- pthread_mutex_lock ``` **Important caveat on interpretation:** this path executes constantly during normal operation (hours of MUD text) without deadlocking, so the plain `ExtTextOut -> GetTextMetrics` nesting cannot by itself be the bug. The defect must be a *conditional* one — most likely either an error path that returns while still holding the font lock (leaking it, so the next unrelated acquisition on the same thread deadlocks), or a slow-path branch that only takes the lock on a cache miss. I could not narrow this further: no debuginfo is published for these builds, so the local functions are unnamed. The offsets above are specific to this build (build-id `923f93fc9612c0597da5d3a7aa26b096aad3d56f`) and are given for orientation only. --- ## Ruled out - **Network / application hang:** the client's TCP socket is `ESTABLISHED` with zero bytes queued in both directions. - **Resource exhaustion:** 76 MB RSS, stable; 4 threads; ~35 s CPU over 4 h 51 m. - **Contention with another thread:** all other threads idle, `__owner` is the waiting thread itself. - **A dead thread holding the lock:** `__owner` names a live thread — the blocked one. --- ## Environment - Wine **11.13**, distro package `wine 11.13-1.1` from CachyOS (`cachyos-extra-v4`), a rebuild of Arch's `extra/wine 11.13-1`. **Not tested against a pristine upstream build** — worth disclosing, since maintainers may ask. - CachyOS (Arch-based), kernel 7.1.4-1-cachyos, x86-64. - KDE Plasma 6 **Wayland** session; the app runs under **`winex11.drv`** via XWayland (`DISPLAY=:0`). - NVIDIA proprietary driver, dual RTX 3090. - Dedicated 64-bit prefix, no DLL overrides, `WINEDEBUG=-all`. - **At the time of the hang**, this prefix had `HKCU\Software\Wine\X11 Driver\ClientSideWithRender = "N"` set (to work around a separate vanishing-glyph problem). That setting forces client-side glyph rasterisation and therefore drives far more traffic through the font engine, so it plausibly increases exposure to this deadlock. -- 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.