From bee21e34470eed7f2d8566b3e70e42afc51cc5c1 Mon Sep 17 00:00:00 2001 From: nhmall Date: Sun, 12 Jan 2025 13:50:25 -0500 Subject: [PATCH] fix K4318 Reported by paxed. A potion of oil, that was already in the midst of exploding, got picked up through spot_effects(), which led to it merging with another potion of oil and the freeing of the original obj. The original obj pointer was still held by breakobj(), and breakobj() proceeded to delete the obj (again). Function nesting: 1 spelleffects() 2 -> weffects() 3 -> bhit() 4 -> bhitpile() 5 -> bhito(obj ...) 6 -> hero_breaks(obj ...) 7 -> breakobj(obj ...) 8 -> explode_oil(obj ...) 9 -> splatter_burning_oil() 10 -> explode() 11 -> zap_over_floor() 12 -> melt_ice() 13 -> spot_effects() 14 -> pickup() 15 -> pickup_object(obj ...) 16 -> pick_obj(obj ...) 17 -> addinv(obj ...) 18 -> addinv_core0(obj ...) 19 -> merged(obj ...) 20 -> obfree(obj ...) 21 -> dealloc_obj(obj ...) 8 -> delobj(obj ...) 9 -> delobj_core(obj ...) 10 -> obfree(obj ...) 11 -> dealloc_obj(obj ...) 12 -> impossible("obj already deleted) This marks the exploding potion with LOST_EXPLODING, so that it won't get picked up, or merged with another object during the long sequence of functions, and that should take care of 15-21 above. --- include/obj.h | 10 ++++++---- src/explode.c | 1 + src/invent.c | 2 ++ src/pickup.c | 6 ++++-- 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/include/obj.h b/include/obj.h index f841725ef..ccab26f6e 100644 --- a/include/obj.h +++ b/include/obj.h @@ -469,10 +469,12 @@ struct obj { #define POTHIT_OTHER_THROW 3 /* propelled by some other means [scatter()] */ /* tracking how an item left your inventory via how_lost field */ -#define LOST_NONE 0 /* still in inventory, or method not covered below */ -#define LOST_THROWN 1 /* thrown or fired by the hero */ -#define LOST_DROPPED 2 /* dropped or tipped out of a container by the hero */ -#define LOST_STOLEN 3 /* stolen from hero's inventory by a monster */ +#define LOST_NONE 0 /* still in inventory, or method not covered below */ +#define LOST_THROWN 1 /* thrown or fired by the hero */ +#define LOST_DROPPED 2 /* dropped or tipped out of a container by the hero */ +#define LOST_STOLEN 3 /* stolen from hero's inventory by a monster */ +#define LOSTOVERRIDEMASK 0x3 +#define LOST_EXPLODING 4 /* the object is exploding (i.e. POT_OIL) */ /* tracking how a name got acquired by an object in named_how field */ #define NAMED_PLAIN 0 /* nothing special, typical naming */ diff --git a/src/explode.c b/src/explode.c index 18bf8d53c..36c3aca59 100644 --- a/src/explode.c +++ b/src/explode.c @@ -976,6 +976,7 @@ explode_oil(struct obj *obj, coordxy x, coordxy y) if (!obj->lamplit) impossible("exploding unlit oil"); end_burn(obj, TRUE); + obj->how_lost = LOST_EXPLODING; splatter_burning_oil(x, y, diluted_oil); } diff --git a/src/invent.c b/src/invent.c index 6f2cc2528..c3e46faad 100644 --- a/src/invent.c +++ b/src/invent.c @@ -4943,6 +4943,8 @@ mergable( if (obj->cursed != otmp->cursed || obj->blessed != otmp->blessed) return FALSE; + if ((obj->how_lost & LOSTOVERRIDEMASK) != 0) + return FALSE; #if 0 /* don't require 'bypass' to match; that results in items dropped * via 'D' not stacking with compatible items already on the floor; * caller who wants that behavior should use 'nomerge' instead */ diff --git a/src/pickup.c b/src/pickup.c index a1c2ccb78..a01283cde 100644 --- a/src/pickup.c +++ b/src/pickup.c @@ -950,6 +950,8 @@ autopick_testobj(struct obj *otmp, boolean calc_costly) return TRUE; if (flags.nopick_dropped && otmp->how_lost == LOST_DROPPED) return FALSE; + if (otmp->how_lost == LOST_EXPLODING) + return FALSE; /* check for pickup_types */ pickit = (!*otypes || strchr(otypes, otmp->oclass)); @@ -1866,7 +1868,7 @@ pickup_object( couldn't pick up a thrown, stolen, or dropped item that was split off from a carried stack even while still carrying the rest of the stack unless we have at least one free slot available */ - obj->how_lost = LOST_NONE; /* affects merge_choice() */ + obj->how_lost &= ~LOSTOVERRIDEMASK; /* affects merge_choice() */ res = lift_object(obj, (struct obj *) 0, &count, telekinesis); obj->how_lost = save_how_lost; /* even when res > 0, * in case we call splitobj() below */ @@ -1879,7 +1881,7 @@ pickup_object( if (obj->quan != count && obj->otyp != LOADSTONE) obj = splitobj(obj, count); - obj->how_lost = LOST_NONE; + obj->how_lost &= ~LOSTOVERRIDEMASK; obj = pick_obj(obj); if (uwep && uwep == obj)