Report module names, not the paths they were loaded from

The module table is what makes a report symbolizable, but a full path also
carries the player's Windows account name and wherever they keep the game,
and the report leaves their machine. Symbolizing only ever matched on module
name and base — symbolize_crash already ran every path through baseName()
before printing it — so the directory was read by nothing.

Say so in the consent prompt too. "No personal information" was not true of
a table full of paths, and is only worth claiming if it holds.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
majcosta
2026-07-28 10:25:47 -03:00
committed by majcosta
co-authored by Claude Opus 5
parent 3794e3a274
commit 90f5bff69b
2 changed files with 25 additions and 10 deletions
+3 -1
View File
@@ -148,7 +148,9 @@ void processCrashTelemetry(const wchar_t* url) {
if (consent < 0) { // first run: ask once, remember the answer
int r = MessageBoxW(NULL,
L"This build can send crash reports to the developers to help fix bugs.\n"
L"A report contains only technical crash data \x2014 no personal information.\n\n"
L"A report contains where the game crashed, the names of the loaded\n"
L"modules, and the HANDLE from your Ja2.ini if you set one. No file\n"
L"paths, no save games, nothing else about your machine.\n\n"
L"Send crash reports automatically?",
L"Jagged Alliance 2 v1.13 \x2014 Crash Reporting",
MB_YESNO | MB_ICONQUESTION);
+22 -9
View File
@@ -304,7 +304,7 @@ void writeExceptionBacktrace(_EXCEPTION_POINTERS* ep) {
// nor anything identifiable in a sibling module: without this table none of
// the addresses below can be symbolized. Walked off the PEB loader list, which
// costs no allocation, unlike EnumProcessModules or DbgHelp's module APIs.
emit(wsprintfA(line, " modules (base size path):\r\n"));
emit(wsprintfA(line, " modules (base size name):\r\n"));
PEB_LDR_DATA* ldr = NtCurrentTeb()->ProcessEnvironmentBlock->Ldr;
LIST_ENTRY* head = &ldr->InMemoryOrderModuleList;
int seen = 0;
@@ -318,15 +318,28 @@ void writeExceptionBacktrace(_EXCEPTION_POINTERS* ep) {
IMAGE_NT_HEADERS* nt = (IMAGE_NT_HEADERS*)(base + dos->e_lfanew);
if (nt->Signature != IMAGE_NT_SIGNATURE)
continue;
// Truncate the path into a terminated buffer: FullDllName is counted, not
// terminated, and wsprintf supports neither "*" precision nor a way to
// bound %S, so a long path would run off the end of both.
WCHAR path[140];
int pathChars = (int)(m->FullDllName.Length / sizeof(WCHAR)) + 1;
lstrcpynW(path, m->FullDllName.Buffer,
pathChars < ARRAYSIZE(path) ? pathChars : ARRAYSIZE(path));
// File name only, never the directory: a full path carries the player's
// Windows account name and whatever else their install path says about
// them, and the report leaves the machine. Symbolizing matches on module
// name and base, so the directory was never read by anything.
//
// Copied into a terminated buffer: FullDllName is counted, not terminated,
// and wsprintf supports neither "*" precision nor a way to bound %S, so a
// long name would run off the end of both.
const WCHAR* full = m->FullDllName.Buffer;
int fullChars = (int)(m->FullDllName.Length / sizeof(WCHAR));
if (!full || fullChars <= 0)
continue;
int start = 0;
for (int i = 0; i < fullChars; ++i)
if (full[i] == L'\\' || full[i] == L'/')
start = i + 1;
WCHAR modname[64];
int nameChars = fullChars - start + 1;
lstrcpynW(modname, full + start,
nameChars < ARRAYSIZE(modname) ? nameChars : ARRAYSIZE(modname));
emit(wsprintfA(line, " %08lX %08lX %S\r\n", base,
nt->OptionalHeader.SizeOfImage, path));
nt->OptionalHeader.SizeOfImage, modname));
}
// Committed stack bounds from the TEB, so a bad ebp can't fault our reads.