From the playtest dump: an access violation reading address zero inside
strncpy, two minutes into a session, dispatched from a window message -
WM_TIMER, wParam 7.
Seven is kLobbyTimerId. That timer runs every 750ms while hosting or
seated, and its work is RefreshRoster -> RPL4Lobby_RosterLines ->
CollectMembers, which reads each member's row out of the lobby.
CollectMembers took Steam's answers straight as strings: eleven strncpy
and atoi calls fed directly from GetLobbyMemberData with nothing between.
Steam answers NULL - not "" - for a member it no longer knows, and the
roster is polled on a timer, so somebody leaving between
GetNumLobbyMembers and the read of their row is ordinary rather than
exotic. strncpy then walks off address zero, which is exactly the
faulting instruction.
The file already knew this: LobbyText() has guarded the lobby-wide reads
from the start, and all six other direct readers test for NULL. Only the
member rows went unguarded. So they get the matching accessor,
MemberText(), and CollectMembers goes through it - which covers the two
atoi calls as well, since atoi(NULL) reads address zero just as happily.
Not confirmed frame by frame: the dump is a tester's own build
(D:\Games\RP412, stamped 13 Aug 16:33) and no matching pdb came with it,
so the frames resolve only as offsets. What is symbol-independent is the
message number and the timer id, read straight off the stack; from there
this is the one path in that handler that does an unguarded strncpy on a
pointer Steam is allowed to return NULL for. The call depth after
inlining matches too.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>