CallFunc_SetThingProperty: Get next mobj in TID chain at start of loop

In cases where state/property set can cause instant deletion, definitely interrupts FindMobjFromTID iteration after one step and potentially uses after free

Also adds comment warnings to this effect near ways to find P_FindMobjFromTID, and updates P_ProcessSpecial even though we could probably stand to rip it out now
This commit is contained in:
toaster 2025-05-26 15:22:30 +01:00
parent 1cdf381aba
commit 5e7cce9047
4 changed files with 15 additions and 5 deletions

View file

@ -3589,13 +3589,15 @@ bool CallFunc_SetThingProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, A
INT32 value = 0;
tag = argV[0];
mobj = P_FindMobjFromTID(tag, mobj, info->mo);
mobj_t *next = P_FindMobjFromTID(tag, mobj, info->mo);
property = argV[1];
value = argV[2];
while (mobj != NULL)
while ((mobj = next) != NULL)
{
// First in case of deletion. (Can't check for value == S_NULL because of A_ calls, etc)
next = P_FindMobjFromTID(tag, mobj, info->mo);
#define PROP_READONLY(x, y) \
case x: \
@ -3830,8 +3832,6 @@ bool CallFunc_SetThingProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, A
}
}
mobj = P_FindMobjFromTID(tag, mobj, info->mo);
#undef PROP_FLAGS
#undef PROP_SCALE
#undef PROP_MOBJ

View file

@ -612,6 +612,9 @@ void P_InitTIDHash(void);
void P_AddThingTID(mobj_t *mo);
void P_RemoveThingTID(mobj_t *mo);
void P_SetThingTID(mobj_t *mo, mtag_t tid);
// This function cannot be safely called after *i is removed!
// Please call at start of loops if *i is to be mutated
mobj_t *P_FindMobjFromTID(mtag_t tid, mobj_t *i, mobj_t *activator);
void P_DeleteMobjStringArgs(mobj_t *mobj);

View file

@ -15417,6 +15417,8 @@ void P_SetThingTID(mobj_t *mo, mtag_t tid)
//
// P_FindMobjFromTID
// Mobj tag search function.
// This function cannot be safely called after *i is removed!
// Please call at start of loops if *i is to be mutated
//
mobj_t *P_FindMobjFromTID(mtag_t tid, mobj_t *i, mobj_t *activator)
{

View file

@ -3275,8 +3275,13 @@ boolean P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, cha
return false;
}
while ((targetThing = P_FindMobjFromTID(args[1], targetThing, mo)) != NULL)
mobj_t *next = P_FindMobjFromTID(args[1], targetThing, mo);
while ((targetThing = next) != NULL)
{
// First in case of deletion. (Can't check for state == S_NULL because of A_ calls, etc)
next = P_FindMobjFromTID(args[1], targetThing, mo);
if (targetThing->player != NULL)
{
continue;