From 9711e61f89715bbd1677eafa7aea4681cb66b820 Mon Sep 17 00:00:00 2001 From: jirik2077 Date: Sat, 18 Jul 2026 21:14:50 +0200 Subject: [PATCH 1/3] fixes heap-use-after-free in objectUseItemOnInternal when object is destoryed by the script (fixing K9 and attaching pole in Mariposa cart) --- src/proto_instance.cc | 66 ++++++++++++++++++++----------------------- 1 file changed, 30 insertions(+), 36 deletions(-) diff --git a/src/proto_instance.cc b/src/proto_instance.cc index 968bca7e..5d91f141 100644 --- a/src/proto_instance.cc +++ b/src/proto_instance.cc @@ -1308,52 +1308,46 @@ UseItemResultCode objectUseItemOnInternal(Object* critter, Object* targetObj, Ob } if (skill == -1) { - Script* script; + // lets store the targetObject/item script id as there's no guarantee + // that targetObject/item is not destroyed and freed within the script + const int targetObjectSid = targetObj->sid; + const int itemSid = item->sid; - if (item->sid == -1) { - if (targetObj->sid == -1) { - return _protinst_default_use_item(critter, targetObj, item); - } + if (itemSid != -1) { + Script* itemScript; - scriptSetObjects(targetObj->sid, critter, item); - scriptExecProc(targetObj->sid, SCRIPT_PROC_USE_OBJ_ON); + scriptSetObjects(itemSid, critter, targetObj); + scriptExecProc(itemSid, SCRIPT_PROC_USE_OBJ_ON); - if (scriptGetScript(targetObj->sid, &script) == -1) { + if (scriptGetScript(itemSid, &itemScript) == -1) { return USE_ITEM_RESULT_ERROR; } - if (!script->scriptOverrides) { - return _protinst_default_use_item(critter, targetObj, item); - } - } else { - scriptSetObjects(item->sid, critter, targetObj); - scriptExecProc(item->sid, SCRIPT_PROC_USE_OBJ_ON); - - if (scriptGetScript(item->sid, &script) == -1) { - return USE_ITEM_RESULT_ERROR; - } - - if (script->returnValue == 0) { - if (targetObj->sid == -1) { - return _protinst_default_use_item(critter, targetObj, item); - } - - scriptSetObjects(targetObj->sid, critter, item); - scriptExecProc(targetObj->sid, SCRIPT_PROC_USE_OBJ_ON); - - Script* script; - if (scriptGetScript(targetObj->sid, &script) == -1) { - return USE_ITEM_RESULT_ERROR; - } - - if (!script->scriptOverrides) { - return _protinst_default_use_item(critter, targetObj, item); - } + if (itemScript->returnValue != 0) { + // FO didn't have any check for return value, and it's probably not needed anyway. + return static_cast(itemScript->returnValue); } } + if (targetObjectSid== -1) { + return _protinst_default_use_item(critter, targetObj, item); + } + + Script* targetScript; + + scriptSetObjects(targetObjectSid, critter, item); + scriptExecProc(targetObjectSid, SCRIPT_PROC_USE_OBJ_ON); + + if (scriptGetScript(targetObjectSid, &targetScript) == -1) { + return USE_ITEM_RESULT_ERROR; + } + + if (!targetScript->scriptOverrides) { + return _protinst_default_use_item(critter, targetObj, item); + } + // FO didn't have any check for return value, and it's probably not needed anyway. - return static_cast(script->returnValue); + return static_cast(targetScript->returnValue); } if (isInCombat()) { From 24562f57daf6a08dfc5f4c72ab276530310c4976 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 18 Jul 2026 19:22:04 +0000 Subject: [PATCH 2/3] chore: auto-format with clang-format --- src/proto_instance.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/proto_instance.cc b/src/proto_instance.cc index 5d91f141..87b520fb 100644 --- a/src/proto_instance.cc +++ b/src/proto_instance.cc @@ -1308,7 +1308,7 @@ UseItemResultCode objectUseItemOnInternal(Object* critter, Object* targetObj, Ob } if (skill == -1) { - // lets store the targetObject/item script id as there's no guarantee + // lets store the targetObject/item script id as there's no guarantee // that targetObject/item is not destroyed and freed within the script const int targetObjectSid = targetObj->sid; const int itemSid = item->sid; @@ -1329,7 +1329,7 @@ UseItemResultCode objectUseItemOnInternal(Object* critter, Object* targetObj, Ob } } - if (targetObjectSid== -1) { + if (targetObjectSid == -1) { return _protinst_default_use_item(critter, targetObj, item); } From 5a87638781d356c05143bbebfcf4f3a5af76d1d8 Mon Sep 17 00:00:00 2001 From: jirik2077 Date: Sun, 19 Jul 2026 06:04:34 +0200 Subject: [PATCH 3/3] review comments --- src/proto_instance.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/proto_instance.cc b/src/proto_instance.cc index 87b520fb..99292a2c 100644 --- a/src/proto_instance.cc +++ b/src/proto_instance.cc @@ -1308,9 +1308,7 @@ UseItemResultCode objectUseItemOnInternal(Object* critter, Object* targetObj, Ob } if (skill == -1) { - // lets store the targetObject/item script id as there's no guarantee - // that targetObject/item is not destroyed and freed within the script - const int targetObjectSid = targetObj->sid; + // store the item script id as there's no guarantee item is not deallocated within the script const int itemSid = item->sid; if (itemSid != -1) { @@ -1329,6 +1327,8 @@ UseItemResultCode objectUseItemOnInternal(Object* critter, Object* targetObj, Ob } } + // store the target object script id as there's no guarantee target object is not deallocated within the script + const int targetObjectSid = targetObj->sid; if (targetObjectSid == -1) { return _protinst_default_use_item(critter, targetObj, item); }