Fix Windows launch crash since v0.5: noexcept LocateFile (issue #58)#118
Merged
Fix Windows launch crash since v0.5: noexcept LocateFile (issue #58)#118
Conversation
Win10 19042 crashed at launch with STATUS_HEAP_CORRUPTION the first time PortInit called Ship::Context::LocateFileAcrossAppDirs (immediately after "ControlDeck OK"). The v0.5.2-beta diagnostic log narrowed the fault to a heap-manager exception fired during a C++ exception unwind, which our vectored handler had been silently dropping under the 0xE06D7363 suppression — so the *triggering* throw was invisible. Two fixes: 1. PortLocateFile (replaces Ship::Context::LocateFileAcrossAppDirs at the three call sites in port.cpp). LUS's version uses the throwing std::filesystem::exists(p) overload, which raises filesystem_error if the underlying GetFileAttributesExW returns anything beyond an ENOENT-equivalent. Under NON_PORTABLE on Windows the first probed path is "<%APPDATA%>\\BattleShip\\/<basename>" — SDL_GetPrefPath returns a backslash-terminated path, joined with a literal "/" yielding mixed separators that on at least Win10 20H2 are rejected with an error severe enough to throw. The unwind from the throw then trips the heap-corruption detector before any user-level catch can run, fast-failing the process. Switch to the noexcept exists(p, ec) overload — false is the right answer for any "does this file exist?" probing failure. Also probe via ssb64::RealAppBundlePath() instead of Ship::Context::GetAppBundlePath(), which under NON_PORTABLE returns the literal "BattleShip" (CMAKE_INSTALL_PREFIX) — wrong for the portable-zip distro the user has unzipped to anywhere. Same defensive shape applied to the in-line std::filesystem::exists on the BattleShip.o2r post-extract probe. 2. Vectored handler now decodes the MSVC C++ throw ABI for code 0xE06D7363 — primary CatchableType -> std::type_info name and, for std::exception derivatives, what(). Logs to ssb64.log capped at 8 per process (in case a hot path throws). Still returns EXCEPTION_CONTINUE_SEARCH so SEH catch handlers run normally; this is purely diagnostic. Cap is by InterlockedIncrement on a static LONG so multi-threaded throws stay bounded. what() decode is wrapped in __try/__except so we never trigger a secondary fault from inside a vectored handler. The libultraship-side fix (mirror the noexcept overload in Context::LocateFileAcrossAppDirs itself) is the right long-term home for #1, but the libultraship fork is out of this session's push scope; folding it into port.cpp avoids the race for now.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Ship::Context::LocateFileAcrossAppDirsat the three port call sites with a port-sidePortLocateFilethat uses the noexceptstd::filesystem::exists(p, ec)overload — fixes immediate-launch crash on Windows reproduced across Win10 19042 (Immediately Crashing on v0.5 (Windows) #58, Girtana1), Win10 22H2 (Immediately Crashing on v0.5 (Windows) #58 comment, DanKorell), and Win11 (today's report).0xE06D7363) in the vectored handler so future bugs of this shape print a readable type name +what()instead of an opaque exception code.Diagnosis
User crash logs (Win11, today) show a first-chance
0xC0000374(STATUS_HEAP_CORRUPTION) immediately after theSSB64: locating f3d.o2r ...checkpoint, with the second-chance arriving as a0xE06D7363MSVC C++ throw. The only code between the checkpoint and the crash isShip::Context::LocateFileAcrossAppDirs("f3d.o2r"), which uses the throwing overload ofstd::filesystem::exists.Under
NON_PORTABLEon Windows the first probed path is<%APPDATA%>\BattleShip\(backslash-terminated fromSDL_GetPrefPath) joined with a literal"/"— yielding mixed separators likeC:\Users\u\AppData\Roaming\BattleShip\/f3d.o2r. On at least three different Windows builds,GetFileAttributesExWreturns an error severe enough to throwfilesystem_error, the unwind trips heap-corruption detection, and the process fast-fails before any user-level catch can run.The fix:
PortLocateFile(new helper) usesexists(p, ec)—falseis the right answer for any "does this file exist?" probing failure. It also probes the bundle directory viassb64::RealAppBundlePath()(real exe dir on Windows portable-zip) instead ofShip::Context::GetAppBundlePath(), which underNON_PORTABLEreturns the literalCMAKE_INSTALL_PREFIX("BattleShip") and is wrong for the portable distribution. The same defensive shape is applied to the inlinestd::filesystem::existson theBattleShip.o2rpost-extract probe.Vectored handler now decodes the MSVC C++ throw ABI for code
0xE06D7363— primaryCatchableType→std::type_info::name()and, forstd::exceptionderivatives,what(). Logs cap at 8 per process. Still returnsEXCEPTION_CONTINUE_SEARCHso SEH catch handlers run normally; this is purely diagnostic.what()decode is wrapped in__try/__exceptso the handler never triggers a secondary fault.Backstory
This commit (
42ad2d6) was authored on May 1 against #58 but was never opened as a PR — issue #58 was closed when the diagnostic-logging commit (edf1440) shipped in v0.5.2-beta, but the actual fix never landed. v0.5/0.6/0.7/0.7.1 all carry the same crash.Test plan
main, all 688 TUs compile + link).0xC0000374.🤖 Generated with Claude Code