Skip to content

On Windows desktop crashes with 0xc00000fd (stack overflow) ~0.4 s after connect, during initial history load — 522.9 KiB stack frame vs 1 MiB main-thread stack #7297

Description

@n-able-consulting

Describe the bug

The Buzz desktop app on Windows x64 crashes with 0xc00000fd (STATUS_STACK_BUFFER_OVERRUN) about 0.4 s after the websocket connects, while the initial channel history is being loaded. The process dies on the main thread: the relay sees the connection and NIP-42 auth succeed, then the client is simply gone. Reproducible — two separate crash dumps show the identical fault.

Steps to reproduce

Install Buzz desktop 0.5.20 on Windows x64.
Connect to a relay with non-trivial channel history (ours: a burst of ~50 EOSE/EVENT history frames on connect).
~0.4 s after connect, during history load, the app crashes.
Windows Event Log (Application, ID 1000): Exception code: 0xc00000fd, faulting module buzz-desktop.exe, fault offset 0x0655b2a6.
Observed on a locally rebuilt desktop-v0.5.20 (mingw cross-build, TLS native-roots fix, 2 MiB main-thread reserve). The stock release ships with a 1 MiB main-thread reserve, so it should hit this at least as easily (the stock binary itself was not tested — its TLS stack can't complete the handshake on our private-CA relay, see #5197).

Expected behavior

History loads and the app stays open. Same relay, same history, 0.5.20 Linux .deb: no crash.

Version and platform

Buzz version: desktop-v0.5.20 (tag desktop-v0.5.20)
OS: Windows 11 x64 (build 10.0.26200), AMD64 (family 6, model 126), 8 CPUs
Control: 0.5.20 Linux .deb on the same relay — no crash (glibc main-thread default is 8 MiB)
Forensics (minidumps + the release binary)

minidump-stackwalk on both dumps (buzz-desktop.exe.33308.dmp, buzz-desktop.exe.22228.dmp):

Crash reason: EXCEPTION_STACK_OVERFLOW
Crash address: 0x00007ff7fb25b2a6
Crashing instruction: test qword [rcx], rcx
Crash address = RVA 0x655b2a6, inside ___chkstk_ms — the compiler's stack-probe helper. The fault is the probe's page-touch loop: the allocation of a large stack frame walked past the guard page.
The faulting frame is a static 522.9 KiB frame. The call site at RVA 0xa6cd85 executes mov $0x82bb8, %eax; call ___chkstk_ms (0x82bb8 = 535,480 bytes); its return address (0xa6cd8e) is exactly frame 1 in both dumps, so this is the function allocating the frame at crash time.
Per the build's COFF symbol table, the enclosing function is core::ptr::drop_in_placereqwest::async_impl::response::Response::do_bytes::{closure} (a ~218 KiB monomorphized drop; it contains no self-recursive calls — this is one giant frame, not deep recursion).
Independent cross-check from the register state in both dumps: at fault rax = 0x5ebb8 and the probe had touched 37 pages, so the initial frame was 0x5ebb8 + 36×0x1000 = 0x82bb8 — the same 522.9 KiB, from two separate dumps.
At the moment that frame was allocated, only ~148 KiB of stack remained below it: the main-thread call chain was already ~1.85 MiB deep.
PE header of the stock release buzz-desktop.exe (Buzz_0.5.20_x64-setup_alpha-unsigned.exe): SizeOfStackReserve = 0x100000 (1 MiB — the Windows default). The same code on Linux gets glibc's 8 MiB main-thread stack.
This is not a one-off in the codebase: a scan of the same binary found ~10 other functions with single-frame reserves of 1024–1094 KiB. With a 1 MiB main-thread reserve, any of them can overflow on a moderately deep call chain.
Why it never happens on Linux

Same code, same history: 8 MiB main-thread reserve, so the ~2.4 MiB of depth fits. Windows ships 1 MiB.

Proof the stack size is the trigger

Rebuilt 0.5.20 with the PE SizeOfStackReserve changed from 2 MiB to 8 MiB (matching the Linux default) — no code changes. The crash no longer reproduces: the app connects, authenticates, and renders the full history. Raising the reserve is therefore a verified workaround; shrinking the frame is the real fix.

Suggested directions

Real fix: find out why that monomorphized drop_in_place needs a ~511 KiB stack frame — likely a large inline buffer inside the dropped future/closure state that belongs on the heap — and shrink it. A drop requiring half a megabyte of stack is a strong smell, and it makes every call into it fragile.
Immediate mitigation: raise the Windows desktop build's main-thread stack reserve to 8 MiB to match Linux (linker flag for the desktop target — upstream CI builds with MSVC: /STACK:8388608; GNU/mingw equivalent: -Wl,--stack,8388608). Given the number of ~1 MiB single-frame allocations in the binary, 1 MiB is structurally insufficient for this codebase on Windows.
Happy to share the two minidumps and the relay-side logs (NIP-42 auth success immediately before the client died), and to test any candidate fix or a release candidate.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions