alliedmodders / sourcemod

SourceMod - Source Engine Scripting and Administration
http://www.sourcemod.net/
974 stars 422 forks source link

Allow to use zero ThisPtr in SDKCall_Raw #1404

Closed dragokas closed 3 years ago

dragokas commented 3 years ago

Help us help you

Environment

Description

I'd like to ask to partially revert PR#1265, that limiting the range of allowed addresses for passing to SDKCall with SDKCall_Raw type.

Here is a using sample, that works well in SM < 1.11.6584:

Problematic Code (or Steps to Reproduce)

sample.games.txt

"Games"
{
    "left4dead"
    {
        "Signatures"
        {
            "GetMissionFirstMap"
            {
                "library"   "server"
                "linux"     "@_ZN16CTerrorGameRules18GetMissionFirstMapEPP9KeyValues"
            }
        }
    }
}

sample.sp

#pragma semicolon 1
#pragma newdecls required

#include <sourcemod>
#include <sdktools>

#define GAMEDATA_1                          "sample.games"

Handle g_hSDK_GetMissionFirstMap;

public void OnPluginStart()
{
    LoadGameData();
    RegConsoleCmd("sm_m", CmdMap);
}

void LoadGameData()
{
    char sPath[PLATFORM_MAX_PATH];
    BuildPath(Path_SM, sPath, sizeof(sPath), "gamedata/%s.txt", GAMEDATA_1);
    if( FileExists(sPath) == false ) SetFailState("Missing required file: \"%s\"", sPath);

    GameData hGameData = new GameData(GAMEDATA_1);
    if( hGameData == null ) SetFailState("Failed to load \"%s.txt\" gamedata.", GAMEDATA_1);

    StartPrepSDKCall(SDKCall_Raw);
    if( PrepSDKCall_SetFromConf(hGameData, SDKConf_Signature, "GetMissionFirstMap") == false )
    {
        LogError("Failed to find signature: GetMissionFirstMap");
    } else {
        PrepSDKCall_SetReturnInfo(SDKType_PlainOldData, SDKPass_Plain);

        g_hSDK_GetMissionFirstMap = EndPrepSDKCall();
        if( g_hSDK_GetMissionFirstMap == null )
            LogError("Failed to create SDKCall: GetMissionFirstMap");
    }
}

Action CmdMap(int client, int args)
{
    int keyvalue = SDKCall(g_hSDK_GetMissionFirstMap, 0);
    ReplyToCommand(client, "kv addr = %i", keyvalue);
    return Plugin_Handled;
}

From SM 1.11.6584+ it raises: ThisPtr address cannot be null I did my best to alter code trying to pass valid g_pGameRules address or zero (using SDKCall_GameRules), without success; lead to crashing. PS. The sample is used as a part of Left 4 DHooks Direct.

dragokas commented 3 years ago

Additional info: Decompiled GetMissionFirstMap

int __cdecl CTerrorGameRules::GetMissionFirstMap(CTerrorGameRules *this)
{
  int v1; // eax
  int v2; // eax
  KeyValues *v3; // esi
  int v4; // ebp
  int v5; // eax
  int v7; // [esp+1Ch] [ebp-30h]
  KeyValues *v8; // [esp+20h] [ebp-2Ch]

  if ( this )
    *(_DWORD *)this = 0;
  v1 = (*(int (__cdecl **)(int))(*(_DWORD *)g_pMatchFramework + 40))(g_pMatchFramework);
  v2 = (*(int (__cdecl **)(int, _DWORD))(*(_DWORD *)v1 + 4))(v1, 0);
  v3 = (KeyValues *)v2;
  if ( !v2 )
    return 0;
  v8 = (KeyValues *)v2;
  v4 = (*(int (__cdecl **)(int, int, CTerrorGameRules *))(*(_DWORD *)g_pMatchExtL4D + 4))(g_pMatchExtL4D, v2, this);
  KeyValues::SetInt(v3, "Game/chapter", 1);
  v5 = (*(int (__cdecl **)(int, KeyValues *, _DWORD))(*(_DWORD *)g_pMatchExtL4D + 4))(g_pMatchExtL4D, v3, 0);
  if ( !v5 )
    v5 = v4;
  v7 = v5;
  KeyValues::deleteThis(v8);
  return v7;
}

How they are using it:

int CRestartGameIssue::ExecuteCommand()
{
  char *v0; // esi
  KeyValues *v1; // eax

  if ( CTerrorGameRules::IsHoldoutMode() )
    return Director::RestartHoldoutRound((Director *)TheDirector);
  v0 = *(char **)(gpGlobals + 60);
  if ( !v0 )
    v0 = "";
  v1 = (KeyValues *)CTerrorGameRules::GetMissionFirstMap(0);
  if ( v1 )
    v0 = (char *)KeyValues::GetString(v1, (const char *)&off_95D879, v0);
  return Director::RestartScenarioFromVote((Director *)TheDirector, v0);
}
dragokas commented 3 years ago

Fixed with VDECODE_FLAG_ALLOWWORLD SDKCall flag.