From 42f2d563a4bc408c037b12e777981088eee72a0e Mon Sep 17 00:00:00 2001 From: Billy Laws Date: Thu, 15 Aug 2024 16:38:19 +0100 Subject: [PATCH] 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. --- lsteamclient/steamclient_main.c | 39 ++++++++++--------- lsteamclient/unix_steam_networking_manual.cpp | 22 +++++------ lsteamclient/unixlib.h | 12 ++++-- 3 files changed, 41 insertions(+), 32 deletions(-) diff --git a/lsteamclient/steamclient_main.c b/lsteamclient/steamclient_main.c index 506e54e9..71fb6d3a 100644 --- a/lsteamclient/steamclient_main.c +++ b/lsteamclient/steamclient_main.c @@ -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 ); diff --git a/lsteamclient/unix_steam_networking_manual.cpp b/lsteamclient/unix_steam_networking_manual.cpp index 4bc6d7b6..cfbaf7d7 100644 --- a/lsteamclient/unix_steam_networking_manual.cpp +++ b/lsteamclient/unix_steam_networking_manual.cpp @@ -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 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); } diff --git a/lsteamclient/unixlib.h b/lsteamclient/unixlib.h index c5f6eea6..1031e6a8 100644 --- a/lsteamclient/unixlib.h +++ b/lsteamclient/unixlib.h @@ -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,