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 );
params.callback->call_cdecl_func_data.pFunc( params.callback->call_cdecl_func_data.data );
break;
case CALL_IFACE_VTABLE_0:
TRACE( "CALL_IFACE_VTABLE_0 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.arg2 );
CALL_VTBL_FUNC( params.callback->call_iface_vtable.iface, 0, 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.arg2) );
break;
case CALL_IFACE_VTABLE_1:
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.arg1, params.callback->call_iface_vtable.arg2 );
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.arg1, params.callback->call_iface_vtable.arg2) );
break;
case CALL_IFACE_VTABLE_2:
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.arg2 );
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.arg2) );
break;
#define CALL_VTABLE_CASES(method) \
case CALL_IFACE_VTABLE_ ## method ## _0: \
TRACE( "CALL_IFACE_VTABLE_" #method "_0 iface %p.\n", 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) ); \
break; \
case CALL_IFACE_VTABLE_ ## method ## _1: \
TRACE( "CALL_IFACE_VTABLE_" #method "_1 iface %p, arg0 %#I64x.\n", params.callback->call_iface_vtable.iface, \
params.callback->call_iface_vtable.arg0 ); \
CALL_VTBL_FUNC( params.callback->call_iface_vtable.iface, method * 4, void, (void *, intptr_t), (params.callback->call_iface_vtable.iface, \
params.callback->call_iface_vtable.arg0) ); \
break; \
case CALL_IFACE_VTABLE_ ## method ## _2: \
TRACE( "CALL_IFACE_VTABLE_" #method "_2 iface %p, arg0 %#I64x, arg1 %#I64x.\n", params.callback->call_iface_vtable.iface, \
params.callback->call_iface_vtable.arg0, params.callback->call_iface_vtable.arg1 ); \
CALL_VTBL_FUNC( params.callback->call_iface_vtable.iface, method * 4, void, (void *, intptr_t, intptr_t), (params.callback->call_iface_vtable.iface, \
params.callback->call_iface_vtable.arg0, params.callback->call_iface_vtable.arg1) ); \
break
CALL_VTABLE_CASES(0);
CALL_VTABLE_CASES(1);
CALL_VTABLE_CASES(2);
#undef CALL_VTABLE_CASES
case CALL_IFACE_VTABLE_0_SERVER_RESPONDED:
TRACE( "CALL_IFACE_VTABLE_0_SERVER_RESPONDED iface %p, server %p.\n", params.callback->server_responded.iface,
params.callback->server_responded.server );

View File

@ -647,17 +647,17 @@ struct SteamMatchmakingServerListResponse_099u : u_ISteamMatchmakingServerListRe
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 )
{
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 )
{
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 );
}
@ -701,17 +701,17 @@ class callback_obj_tracker<SteamMatchmakingServerListResponse_106> SteamMatchmak
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 )
{
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 )
{
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 );
}
@ -744,7 +744,7 @@ void SteamMatchmakingPingResponse::ServerResponded( gameserveritem_t_105 *server
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);
free_callback_obj(this);
}
@ -776,14 +776,14 @@ void SteamMatchmakingPlayersResponse::AddPlayerToList(const char *pchName, int n
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);
free_callback_obj(this);
}
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);
free_callback_obj(this);
}
@ -815,14 +815,14 @@ void SteamMatchmakingRulesResponse::RulesResponded(const char *pchRule, const ch
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);
free_callback_obj(this);
}
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);
free_callback_obj(this);
}

View File

@ -37,9 +37,15 @@ enum callback_type
SOCKETS_DEBUG_OUTPUT = 1,
WARNING_MESSAGE_HOOK,
CALL_CDECL_FUNC_DATA,
CALL_IFACE_VTABLE_0,
CALL_IFACE_VTABLE_1,
CALL_IFACE_VTABLE_2,
CALL_IFACE_VTABLE_0_0,
CALL_IFACE_VTABLE_0_1,
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_ADD_PLAYER_TO_LIST,
CALL_IFACE_VTABLE_0_RULES_RESPONDED,