Check ScriptClassDesc_t of squirrel instance for native member function call

This enhances the ScriptFuncDescriptor_t to record the ScriptClassDesc_t
of the class for which it gets invoked.
The ScriptClassDesc_t are traversed in the function_stub() for native
function calls for member functions, to ensure the passed in instance
has a compatible type.
This prevents memory errors when incorrectly invoking native member
functions from Squirrel.
This commit is contained in:
Alexander 'z33ky' Hirsch 2025-06-11 02:14:13 +02:00
parent c851fc9bfb
commit 681a75a6a7
3 changed files with 22 additions and 6 deletions

View File

@ -263,6 +263,8 @@ inline const char * ScriptFieldTypeName( int16 eType)
//--------------------------------------------------------- //---------------------------------------------------------
struct ScriptClassDesc_t;
struct ScriptFuncDescriptor_t struct ScriptFuncDescriptor_t
{ {
ScriptFuncDescriptor_t() ScriptFuncDescriptor_t()
@ -270,11 +272,13 @@ struct ScriptFuncDescriptor_t
m_pszFunction = NULL; m_pszFunction = NULL;
m_ReturnType = FIELD_TYPEUNKNOWN; m_ReturnType = FIELD_TYPEUNKNOWN;
m_pszDescription = NULL; m_pszDescription = NULL;
m_pScriptClassDesc = NULL;
} }
const char *m_pszScriptName; const char *m_pszScriptName;
const char *m_pszFunction; const char *m_pszFunction;
const char *m_pszDescription; const char *m_pszDescription;
ScriptClassDesc_t *m_pScriptClassDesc;
ScriptDataType_t m_ReturnType; ScriptDataType_t m_ReturnType;
CUtlVector<ScriptDataType_t> m_Parameters; CUtlVector<ScriptDataType_t> m_Parameters;
}; };

View File

@ -61,7 +61,7 @@ FUNC_GENERATE_ALL( DEFINE_MEMBER_FUNC_TYPE_DEDUCER );
FUNC_GENERATE_ALL( DEFINE_CONST_MEMBER_FUNC_TYPE_DEDUCER ); FUNC_GENERATE_ALL( DEFINE_CONST_MEMBER_FUNC_TYPE_DEDUCER );
#define ScriptInitMemberFuncDescriptor_( pDesc, class, func, scriptName ) if ( 0 ) {} else { (pDesc)->m_pszScriptName = scriptName; (pDesc)->m_pszFunction = #func; ScriptDeduceFunctionSignature( pDesc, (class *)(0), &class::func ); } #define ScriptInitMemberFuncDescriptor_( pDesc, class, func, scriptName ) if ( 0 ) {} else { (pDesc)->m_pszScriptName = scriptName; (pDesc)->m_pszFunction = #func; ScriptDeduceFunctionSignature( pDesc, (class *)(0), &class::func ); (pDesc)->m_pScriptClassDesc = GetScriptDesc<class>(nullptr); }
#define ScriptInitFuncDescriptorNamed( pDesc, func, scriptName ) if ( 0 ) {} else { (pDesc)->m_pszScriptName = scriptName; (pDesc)->m_pszFunction = #func; ScriptDeduceFunctionSignature( pDesc, &func ); } #define ScriptInitFuncDescriptorNamed( pDesc, func, scriptName ) if ( 0 ) {} else { (pDesc)->m_pszScriptName = scriptName; (pDesc)->m_pszFunction = #func; ScriptDeduceFunctionSignature( pDesc, &func ); }
#define ScriptInitFuncDescriptor( pDesc, func ) ScriptInitFuncDescriptorNamed( pDesc, func, #func ) #define ScriptInitFuncDescriptor( pDesc, func ) ScriptInitFuncDescriptorNamed( pDesc, func, #func )

View File

@ -1427,18 +1427,30 @@ SQInteger function_stub(HSQUIRRELVM vm)
if (pFunc->m_flags & SF_MEMBER_FUNC) if (pFunc->m_flags & SF_MEMBER_FUNC)
{ {
SQUserPointer self; ClassInstanceData* classInstanceData;
if (SQ_FAILED(sq_getinstanceup(vm, 1, &self, 0))) if (SQ_FAILED(sq_getinstanceup(vm, 1, (SQUserPointer*)&classInstanceData, 0)))
{ {
return sq_throwerror(vm, "Expected class userpointer"); return SQ_ERROR;
} }
if (!self) if (!classInstanceData)
{ {
return sq_throwerror(vm, "Accessed null instance"); return sq_throwerror(vm, "Accessed null instance");
} }
instance = ((ClassInstanceData*)self)->instance; // check that the type of self, or any basetype, matches the function description
ScriptClassDesc_t *selfType = classInstanceData->desc;
while (selfType != pFunc->m_desc.m_pScriptClassDesc)
{
if (!selfType)
{
return sq_throwerror(vm, "Mismatched instance type");
}
selfType = selfType->m_pBaseDesc;
Assert(selfType != classInstanceData->desc); // there should be no infinite loop
}
instance = classInstanceData->instance;
} }
ScriptVariant_t script_retval; ScriptVariant_t script_retval;