Access Violation during datatable encoding/decoding when the local network backdoor is enabled.
Attempting to compile any of the included singleplayer games (hl2, episodic, lostcoast) and run the resulting mod causes an Access Violation / Segmentation Fault in SendProxy_AnimTime, as pStruct is set to -1 whenever a server starts.
Previously, the singleplayer games worked fine even on the multiplayer branch and were even able to take advantage of new features.
This error does not trigger when maxplayers is set to 2, or when cl_localnetworkbackdoor is set to 0, implying the issue is with the local network backdoor part of the engine, which is disabled in multiplayer.
Might also affect the "video stress test" in CS:S, according to a VDC edit. https://developer.valvesoftware.com/w/index.php?title=Source_2013&diff=next&oldid=464793
Might also affect the "video stress test" in CS:S, according to a VDC edit. https://developer.valvesoftware.com/w/index.php?title=Source_2013&diff=next&oldid=464793
Doesn't seem to affect Video Stress Test on Source Engine Test (sourcetest) on updated Source 2013 Base MP (also on TF2 branch) through. Both maxplayers 1 and cl_localnetworkbackdoor 1.
Doesn't seem to affect Video Stress Test on Source Engine Test (sourcetest) on updated Source 2013 Base MP (also on TF2 branch)
That is because sourcetest only launches the 32-bit version (hl2.exe) when launched from steam, even if you're on a 64-bit OS
Doesn't seem to affect Video Stress Test on Source Engine Test (sourcetest) on updated Source 2013 Base MP (also on TF2 branch)
That is because sourcetest only launches the 32-bit version (hl2.exe) when launched from steam, even if you're on a 64-bit OS
Can confirm aswell. CS: Source Video Stress Test didn't crash on 32-bit, but does on 64-bit.
CS: Source Video Stress Test didn't crash on 32-bit, but does on 64-bit.
It crashes on 64-bit if cl_localnetworkbackdoor is set to 1
(The crash also occurs on the prerelease branch)
Also, cl_localnetworkbackdoor is set to 1 by default
I wonder, what does cl_localnetworkbackdoor even do? Enables some network optimizations in singleplayer doesn't help, it just vague.
Like what does it do exactly?
It makes data tables get transmitted by directly performing memcopies to the local client instead of doing serialization and sending via a local network loopback which is slower.
~~I reverse engineered related functions and I believe the issue is due to the struct base pointer getting cast to an int when used in prop offset computation. While this is valid when targeting 32-bit due to an int and a pointer having the same size, when targeting 64-bit a pointer is 64-bits and an int is 32-bits, so after a cast the higher bits get truncated. This causes the offsets to become garbage.~~
~~Here's a snippet of the disassembly as pseudo-C:~~
//...
int64 nStructBase = var_878[ ( uint64 ) * ( uint8 * )( *( uint64 * )( ( char * )pStructBaseMaybe + 0x68 ) + i_5 ) ];
if ( nStructBase )
{
int32 r12_1 = 0;
void *pPropMaybe = *( uint64 * )( *( uint64 * )( ( char * )pPrecalc + 0x48 ) + rbp_1 );
int32 j_2 = 1;
int32 nOffsetMaybe = *( uint32 * )( ( char * )pPropMaybe + 0x70 ) - 1 + ( uint32 )nStructBase;
if ( *( uint32 * )( ( char * )pPropMaybe + 0x10 ) == 5 )
{
r12_1 = *( uint32 * )( ( char * )pPropMaybe + 0x34 );
j_2 = *( uint32 * )( ( char * )pPropMaybe + 0x30 );
nOffsetMaybe = *( uint32 * )( *( uint64 * )( ( char * )pPropMaybe + 0x20 ) + 0x70 ) - 1 + ( uint32 )nStructBase;
}
//...
~~Probably how the affected part looks in the source code:~~
int nOffsetMaybe = pPropMaybe->GetOffset() + ( int )pStructBase - 1;
~~Should be something like this instead:~~
int nOffsetMaybe = pPropMaybe->GetOffset() + ( intp )pStructBase - 1;
~~Same goes for when handling array prop offsets~~
Replying to https://github.com/ValveSoftware/source-sdk-2013/issues/610#issuecomment-3181053579
This is not correct, the offsets are always within 32-bit range. The issue is caused by a 32-bit comparison being performed instead of 64-bit comparison in other parts of engine code
The following patches can be temporarily used to fix local network backdoor (as well as an issue with the signon buffer being too small for TF2, and GC being unavailable) on SDK2013. Windows only, Linux is an exercise for the reader:
mem::pointer ptr_CBaseServer__Clear_signon = mem::scan( mem::pattern( "75 4A 48 8B 07" ), executable_engine );
mem::pointer ptr_LocalTransfer_TransferEntity = mem::scan( mem::pattern( "48 89 5C 24 ? 48 89 74 24 ? 48 89 7C 24 ? 55 41 54 41 55 41 56 41 57 48 8D AC 24 ? ? ? ? B8" ), executable_engine );
mem::pointer ptr_SV_ActivateServer_Steam = mem::scan( mem::pattern( "7E 6D E8 ?? ?? ?? ?? " ), executable_engine );
if ( ptr_CBaseServer__Clear_signon && ptr_LocalTransfer_TransferEntity && ptr_SV_ActivateServer_Steam )
{
// use MP buffer size
// jnz -> jmp
{
mem::protect write( { ptr_CBaseServer__Clear_signon, 1 } );
*ptr_CBaseServer__Clear_signon.as<byte*>() = 0xEB;
}
// fix signed 32bit comparison
// mov r10d, 0FFFFFFFFh
// ->
// xor r10, r10
// not r10
{
mem::region rgn_LocalTransfer_TransferEntity = { ptr_LocalTransfer_TransferEntity, 0x4A2 };
std::vector< mem::pointer > vec_Sequences = mem::scan_all( mem::pattern( "41 BA FF FF FF FF" ), rgn_LocalTransfer_TransferEntity );
if ( vec_Sequences.size() == 5 )
{
byte payload [] = { 0x4d, 0x31, 0xd2, 0x49, 0xf7, 0xd2 };
for ( mem::pointer ptr : vec_Sequences )
{
mem::protect write( { ptr, sizeof( payload ) } );
memcpy( ptr.as<void*>(), payload, sizeof( payload ) );
}
}
}
// keep steam connected in singleplayer for GC
{
// jle -> nop
mem::protect write( { ptr_SV_ActivateServer_Steam, 2 } );
write.fill( NOP );
}
}
Note that for TF2 there is still several other singleplayer-related issues on the game side, which I will make a PR for the in the future, but this allows atleast booting maps successfully.