block / block/buzz

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

Open
#7,297 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Rust
Stars
32.7k
Forks
4.3k
Avg merge
1d 13h
Merged PRs (30d)
253

Description

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_place (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.

Contributor guide

Open the contributing guide

Research direction

Start by reproducing the Windows desktop-v0.5.20 crash during initial history loading, then inspect the desktop target's linker stack-reserve setting and the reqwest::async_impl::response::Response::do_bytes closure identified in the dumps. Done means history loading completes without a stack overflow on Windows; either the large frame is reduced or the Windows stack reserve is raised and verified in a release build.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
desktop, operating-systems
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.