steamclient: Call Windows vtable callbacks with the correct arg count

The x86 Windows ABI pushes all arguments onto the stack in the caller
and expects the callee to clean them up. The mismatch in parameter
counts thus causes the stack to be offset after calling these
callbacks leading to a crash.
This commit is contained in:
Billy Laws 2024-08-15 16:38:19 +01:00 committed by Arkadiusz Hiler
parent 454533f7e3
commit 42f2d563a4
3 changed files with 41 additions and 32 deletions

View File

@ -401,25 +401,28 @@ void execute_pending_callbacks(void)
TRACE( "CALL_CDECL_FUNC_DATA func %p, data %p.\n", params.callback->call_cdecl_func_data.pFunc, params.callback->call_cdecl_func_data.data ); TRACE( "CALL_CDECL_FUNC_DATA func %p, data %p.\n", params.callback->call_cdecl_func_data.pFunc, params.callback->call_cdecl_func_data.data );
params.callback->call_cdecl_func_data.pFunc( params.callback->call_cdecl_func_data.data ); params.callback->call_cdecl_func_data.pFunc( params.callback->call_cdecl_func_data.data );
break; break;
case CALL_IFACE_VTABLE_0: #define CALL_VTABLE_CASES(method) \
TRACE( "CALL_IFACE_VTABLE_0 iface %p, arg0 %#I64x, arg1 %#I64x, arg2 %#I64x.\n", params.callback->call_iface_vtable.iface, case CALL_IFACE_VTABLE_ ## method ## _0: \
params.callback->call_iface_vtable.arg0, params.callback->call_iface_vtable.arg1, params.callback->call_iface_vtable.arg2 ); TRACE( "CALL_IFACE_VTABLE_" #method "_0 iface %p.\n", params.callback->call_iface_vtable.iface ); \
CALL_VTBL_FUNC( params.callback->call_iface_vtable.iface, 0, void, (void *, intptr_t, intptr_t, intptr_t), (params.callback->call_iface_vtable.iface, CALL_VTBL_FUNC( params.callback->call_iface_vtable.iface, method * 4, void, (void *), (params.callback->call_iface_vtable.iface) ); \
params.callback->call_iface_vtable.arg0, params.callback->call_iface_vtable.arg1, params.callback->call_iface_vtable.arg2) ); break; \
break; case CALL_IFACE_VTABLE_ ## method ## _1: \
case CALL_IFACE_VTABLE_1: TRACE( "CALL_IFACE_VTABLE_" #method "_1 iface %p, arg0 %#I64x.\n", params.callback->call_iface_vtable.iface, \
TRACE( "CALL_IFACE_VTABLE_1 iface %p, arg0 %#I64x, arg1 %#I64x, arg2 %#I64x.\n", params.callback->call_iface_vtable.iface, params.callback->call_iface_vtable.arg0 ); \
params.callback->call_iface_vtable.arg0, params.callback->call_iface_vtable.arg1, params.callback->call_iface_vtable.arg2 ); CALL_VTBL_FUNC( params.callback->call_iface_vtable.iface, method * 4, void, (void *, intptr_t), (params.callback->call_iface_vtable.iface, \
CALL_VTBL_FUNC( params.callback->call_iface_vtable.iface, 4, void, (void *, intptr_t, intptr_t, intptr_t), (params.callback->call_iface_vtable.iface, params.callback->call_iface_vtable.arg0) ); \
params.callback->call_iface_vtable.arg0, params.callback->call_iface_vtable.arg1, params.callback->call_iface_vtable.arg2) ); break; \
break; case CALL_IFACE_VTABLE_ ## method ## _2: \
case CALL_IFACE_VTABLE_2: TRACE( "CALL_IFACE_VTABLE_" #method "_2 iface %p, arg0 %#I64x, arg1 %#I64x.\n", params.callback->call_iface_vtable.iface, \
TRACE( "CALL_IFACE_VTABLE_2 iface %p, arg0 %#I64x, arg1 %#I64x, arg2 %#I64x.\n", params.callback->call_iface_vtable.iface, params.callback->call_iface_vtable.arg0, params.callback->call_iface_vtable.arg1 ); \
params.callback->call_iface_vtable.arg0, params.callback->call_iface_vtable.arg1, params.callback->call_iface_vtable.arg2 ); CALL_VTBL_FUNC( params.callback->call_iface_vtable.iface, method * 4, void, (void *, intptr_t, intptr_t), (params.callback->call_iface_vtable.iface, \
CALL_VTBL_FUNC( params.callback->call_iface_vtable.iface, 8, void, (void *, intptr_t, intptr_t, intptr_t), (params.callback->call_iface_vtable.iface, params.callback->call_iface_vtable.arg0, params.callback->call_iface_vtable.arg1) ); \
params.callback->call_iface_vtable.arg0, params.callback->call_iface_vtable.arg1, params.callback->call_iface_vtable.arg2) ); break
break;
CALL_VTABLE_CASES(0);
CALL_VTABLE_CASES(1);
CALL_VTABLE_CASES(2);
#undef CALL_VTABLE_CASES
case CALL_IFACE_VTABLE_0_SERVER_RESPONDED: case CALL_IFACE_VTABLE_0_SERVER_RESPONDED:
TRACE( "CALL_IFACE_VTABLE_0_SERVER_RESPONDED iface %p, server %p.\n", params.callback->server_responded.iface, TRACE( "CALL_IFACE_VTABLE_0_SERVER_RESPONDED iface %p, server %p.\n", params.callback->server_responded.iface,
params.callback->server_responded.server ); params.callback->server_responded.server );

View File

@ -647,17 +647,17 @@ struct SteamMatchmakingServerListResponse_099u : u_ISteamMatchmakingServerListRe
void SteamMatchmakingServerListResponse_099u::ServerResponded( int32_t iServer ) void SteamMatchmakingServerListResponse_099u::ServerResponded( int32_t iServer )
{ {
queue_vtable_callback( this->w_iface, CALL_IFACE_VTABLE_0, (intptr_t)iServer, 0, 0 ); queue_vtable_callback( this->w_iface, CALL_IFACE_VTABLE_0_1, (intptr_t)iServer, 0, 0 );
} }
void SteamMatchmakingServerListResponse_099u::ServerFailedToRespond( int32_t iServer ) void SteamMatchmakingServerListResponse_099u::ServerFailedToRespond( int32_t iServer )
{ {
queue_vtable_callback( this->w_iface, CALL_IFACE_VTABLE_1, (intptr_t)iServer, 0, 0 ); queue_vtable_callback( this->w_iface, CALL_IFACE_VTABLE_1_1, (intptr_t)iServer, 0, 0 );
} }
void SteamMatchmakingServerListResponse_099u::RefreshComplete( uint32_t response ) void SteamMatchmakingServerListResponse_099u::RefreshComplete( uint32_t response )
{ {
queue_vtable_callback( this->w_iface, CALL_IFACE_VTABLE_2, (intptr_t)response, 0, 0 ); queue_vtable_callback( this->w_iface, CALL_IFACE_VTABLE_2_1, (intptr_t)response, 0, 0 );
TRACE( "RefreshComplete this %p, w_iface %p.\n", this, this->w_iface ); TRACE( "RefreshComplete this %p, w_iface %p.\n", this, this->w_iface );
} }
@ -701,17 +701,17 @@ class callback_obj_tracker<SteamMatchmakingServerListResponse_106> SteamMatchmak
void SteamMatchmakingServerListResponse_106::ServerResponded( void *hRequest, int32_t iServer ) void SteamMatchmakingServerListResponse_106::ServerResponded( void *hRequest, int32_t iServer )
{ {
queue_vtable_callback( this->w_iface, CALL_IFACE_VTABLE_0, (intptr_t)hRequest, (intptr_t)iServer, 0 ); queue_vtable_callback( this->w_iface, CALL_IFACE_VTABLE_0_2, (intptr_t)hRequest, (intptr_t)iServer, 0 );
} }
void SteamMatchmakingServerListResponse_106::ServerFailedToRespond( void *hRequest, int32_t iServer ) void SteamMatchmakingServerListResponse_106::ServerFailedToRespond( void *hRequest, int32_t iServer )
{ {
queue_vtable_callback( this->w_iface, CALL_IFACE_VTABLE_1, (intptr_t)hRequest, (intptr_t)iServer, 0 ); queue_vtable_callback( this->w_iface, CALL_IFACE_VTABLE_1_2, (intptr_t)hRequest, (intptr_t)iServer, 0 );
} }
void SteamMatchmakingServerListResponse_106::RefreshComplete( void *hRequest, uint32_t response ) void SteamMatchmakingServerListResponse_106::RefreshComplete( void *hRequest, uint32_t response )
{ {
queue_vtable_callback( this->w_iface, CALL_IFACE_VTABLE_2, (intptr_t)hRequest, (intptr_t)response, 0 ); queue_vtable_callback( this->w_iface, CALL_IFACE_VTABLE_2_2, (intptr_t)hRequest, (intptr_t)response, 0 );
TRACE( "RefreshComplete this %p, w_iface %p.\n", this, this->w_iface ); TRACE( "RefreshComplete this %p, w_iface %p.\n", this, this->w_iface );
} }
@ -744,7 +744,7 @@ void SteamMatchmakingPingResponse::ServerResponded( gameserveritem_t_105 *server
void SteamMatchmakingPingResponse::ServerFailedToRespond(void) void SteamMatchmakingPingResponse::ServerFailedToRespond(void)
{ {
queue_vtable_callback( this->w_iface, CALL_IFACE_VTABLE_1, 0, 0, 0 ); queue_vtable_callback( this->w_iface, CALL_IFACE_VTABLE_1_0, 0, 0, 0 );
TRACE("Deleting this %p, w_iface %p.\n", this, this->w_iface); TRACE("Deleting this %p, w_iface %p.\n", this, this->w_iface);
free_callback_obj(this); free_callback_obj(this);
} }
@ -776,14 +776,14 @@ void SteamMatchmakingPlayersResponse::AddPlayerToList(const char *pchName, int n
void SteamMatchmakingPlayersResponse::PlayersFailedToRespond(void) void SteamMatchmakingPlayersResponse::PlayersFailedToRespond(void)
{ {
queue_vtable_callback( this->w_iface, CALL_IFACE_VTABLE_1, 0, 0, 0 ); queue_vtable_callback( this->w_iface, CALL_IFACE_VTABLE_1_0, 0, 0, 0 );
TRACE("Deleting this %p, w_iface %p.\n", this, this->w_iface); TRACE("Deleting this %p, w_iface %p.\n", this, this->w_iface);
free_callback_obj(this); free_callback_obj(this);
} }
void SteamMatchmakingPlayersResponse::PlayersRefreshComplete(void) void SteamMatchmakingPlayersResponse::PlayersRefreshComplete(void)
{ {
queue_vtable_callback( this->w_iface, CALL_IFACE_VTABLE_2, 0, 0, 0 ); queue_vtable_callback( this->w_iface, CALL_IFACE_VTABLE_2_0, 0, 0, 0 );
TRACE("Deleting this %p, w_iface %p.\n", this, this->w_iface); TRACE("Deleting this %p, w_iface %p.\n", this, this->w_iface);
free_callback_obj(this); free_callback_obj(this);
} }
@ -815,14 +815,14 @@ void SteamMatchmakingRulesResponse::RulesResponded(const char *pchRule, const ch
void SteamMatchmakingRulesResponse::RulesFailedToRespond(void) void SteamMatchmakingRulesResponse::RulesFailedToRespond(void)
{ {
queue_vtable_callback( this->w_iface, CALL_IFACE_VTABLE_1, 0, 0, 0 ); queue_vtable_callback( this->w_iface, CALL_IFACE_VTABLE_1_0, 0, 0, 0 );
TRACE("Deleting this %p, w_iface %p.\n", this, this->w_iface); TRACE("Deleting this %p, w_iface %p.\n", this, this->w_iface);
free_callback_obj(this); free_callback_obj(this);
} }
void SteamMatchmakingRulesResponse::RulesRefreshComplete(void) void SteamMatchmakingRulesResponse::RulesRefreshComplete(void)
{ {
queue_vtable_callback( this->w_iface, CALL_IFACE_VTABLE_2, 0, 0, 0 ); queue_vtable_callback( this->w_iface, CALL_IFACE_VTABLE_2_0, 0, 0, 0 );
TRACE("Deleting this %p, w_iface %p.\n", this, this->w_iface); TRACE("Deleting this %p, w_iface %p.\n", this, this->w_iface);
free_callback_obj(this); free_callback_obj(this);
} }

View File

@ -37,9 +37,15 @@ enum callback_type
SOCKETS_DEBUG_OUTPUT = 1, SOCKETS_DEBUG_OUTPUT = 1,
WARNING_MESSAGE_HOOK, WARNING_MESSAGE_HOOK,
CALL_CDECL_FUNC_DATA, CALL_CDECL_FUNC_DATA,
CALL_IFACE_VTABLE_0, CALL_IFACE_VTABLE_0_0,
CALL_IFACE_VTABLE_1, CALL_IFACE_VTABLE_0_1,
CALL_IFACE_VTABLE_2, CALL_IFACE_VTABLE_0_2,
CALL_IFACE_VTABLE_1_0,
CALL_IFACE_VTABLE_1_1,
CALL_IFACE_VTABLE_1_2,
CALL_IFACE_VTABLE_2_0,
CALL_IFACE_VTABLE_2_1,
CALL_IFACE_VTABLE_2_2,
CALL_IFACE_VTABLE_0_SERVER_RESPONDED, CALL_IFACE_VTABLE_0_SERVER_RESPONDED,
CALL_IFACE_VTABLE_0_ADD_PLAYER_TO_LIST, CALL_IFACE_VTABLE_0_ADD_PLAYER_TO_LIST,
CALL_IFACE_VTABLE_0_RULES_RESPONDED, CALL_IFACE_VTABLE_0_RULES_RESPONDED,