From baff3bc88b64d5681e2d2ac9f7ba10e27ffe1c3f Mon Sep 17 00:00:00 2001 From: "nethack.rankin" Date: Thu, 8 Dec 2005 05:45:43 +0000 Subject: [PATCH] fix object lost panic (trunc only) While testing some killer_xname() changes, I noticed that it was feasible to avoid having some gear destroyed by causing a hangup after getting the destruction message. And while testing the fix for that, I stumbled across a panic situation (not caused by my changes). If you survive entering lava while wearing water walking boots (and aren't fire resistant yourself, and don't have enough hit points to survive 6d6 damage, and your boots aren't fireproofed...), having those boots be destroyed will dump you back into the same lava recursively (lava_effects -> Boots_off -> spoteffects -> lava_effects). And if you survive that (wizard/explore mode or life-saving), there will be a panic when finishing deletion of the boots (useupall) because the recursive call will have already done it (since they aren't worn anymore when inner call handles them, no additional recursion gets triggered and object deletion happens). Some of the other stuff I was working on is mixed in here because this is the configuration I ended up using to test the panic fix. Several Makefiles are missing the dependency for context.h (post-3.4.3 revision). If yours is, then you'll need to force a full rebuild after applying this or you'll end up with havoc. (Mine was, but I noticed that the expected full build wasn't happening and interrupted it to fix that.) --- doc/fixes34.4 | 2 ++ include/context.h | 1 + src/do_wear.c | 65 ++++++++++++++++++++++++++++------------------- src/read.c | 18 ++++++++----- src/trap.c | 23 ++++++++++++++--- 5 files changed, 72 insertions(+), 37 deletions(-) diff --git a/doc/fixes34.4 b/doc/fixes34.4 index 300998f01..87b403319 100644 --- a/doc/fixes34.4 +++ b/doc/fixes34.4 @@ -167,6 +167,8 @@ a samurai quest guardian message used "ninja" where "ronin" was intended revive from fainting if vault guard or bribe-demanding demon approaches tame flaming spheres and shocking spheres shouldn't pick up items can hear the opening or closing of an unseen drawbridge +prevent "object lost" panic caused by accessing freed memory after worn + non-fireproof water walking boots are destroyed by lava Platform- and/or Interface-Specific Fixes diff --git a/include/context.h b/include/context.h index 3e8ae3781..676cabf54 100644 --- a/include/context.h +++ b/include/context.h @@ -80,6 +80,7 @@ struct context_info { /* 8: travel */ unsigned startingpet_mid; int warnlevel; + int in_lava_effects; /* hack for Boots_off() */ long next_attrib_check; /* next attribute check */ long stethoscope_move; short stethoscope_movement; diff --git a/src/do_wear.c b/src/do_wear.c index 402492ef9..43cd21285 100644 --- a/src/do_wear.c +++ b/src/do_wear.c @@ -1,4 +1,4 @@ -/* SCCS Id: @(#)do_wear.c 3.5 2004/11/11 */ +/* SCCS Id: @(#)do_wear.c 3.5 2005/12/07 */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -35,6 +35,7 @@ STATIC_PTR int NDECL(Boots_on); STATIC_DCL int NDECL(Cloak_on); STATIC_PTR int NDECL(Helmet_on); STATIC_PTR int NDECL(Gloves_on); +STATIC_DCL void FDECL(wielding_corpse, (struct obj *,BOOLEAN_P)); STATIC_PTR int NDECL(Shield_on); #ifdef TOURIST STATIC_PTR int NDECL(Shirt_on); @@ -151,7 +152,9 @@ Boots_off() /* check for lava since fireproofed boots make it viable */ if ((is_pool(u.ux, u.uy) || is_lava(u.ux, u.uy)) && !Levitation && !Flying && !is_clinger(youmonst.data) && - !context.takeoff.cancelled_don) { + !context.takeoff.cancelled_don && + /* avoid recursive call to lava_effects() */ + !context.in_lava_effects) { /* make boots known in case you survive the drowning */ makeknown(otyp); spoteffects(TRUE); @@ -407,6 +410,29 @@ Gloves_on() return 0; } +STATIC_OVL void +wielding_corpse(obj, voluntary) +struct obj *obj; +boolean voluntary; /* taking gloves off on purpose? */ +{ + char kbuf[BUFSZ]; + + if (!obj || obj->otyp != CORPSE) return; + if (obj != uwep && (obj != uswapwep || !u.twoweap)) return; + + if (touch_petrifies(&mons[obj->corpsenm]) && !Stone_resistance) { + You("now wield %s in your bare %s.", + the(corpse_xname(obj, TRUE)), + makeplural(body_part(HAND))); + Sprintf(kbuf, "%s gloves while wielding %s", + voluntary ? "removing" : "losing", + killer_xname(obj)); + instapetrify(kbuf); + /* life-saved; can't continue wielding cockatrice corpse though */ + remove_worn_item(obj, FALSE); + } +} + int Gloves_off() { @@ -414,6 +440,7 @@ Gloves_off() u.uprops[objects[uarmg->otyp].oc_oprop].extrinsic & ~WORN_GLOVES; context.takeoff.mask &= ~W_ARMG; + boolean on_purpose = !context.mon_moving && !uarmg->in_use; switch(uarmg->otyp) { case LEATHER_GLOVES: @@ -435,30 +462,15 @@ Gloves_off() context.takeoff.cancelled_don = FALSE; (void) encumber_msg(); /* immediate feedback for GoP */ - /* Prevent wielding cockatrice when not wearing gloves */ - if (uwep && uwep->otyp == CORPSE && - touch_petrifies(&mons[uwep->corpsenm])) { - char kbuf[BUFSZ]; + /* prevent wielding cockatrice when not wearing gloves */ + if (uwep && uwep->otyp == CORPSE) + wielding_corpse(uwep, on_purpose); - You("wield the %s in your bare %s.", - corpse_xname(uwep, TRUE), makeplural(body_part(HAND))); - Strcpy(kbuf, an(corpse_xname(uwep, TRUE))); - instapetrify(kbuf); - uwepgone(); /* life-saved still doesn't allow touching cockatrice */ - } - - /* KMH -- ...or your secondary weapon when you're wielding it */ - if (u.twoweap && uswapwep && uswapwep->otyp == CORPSE && - touch_petrifies(&mons[uswapwep->corpsenm])) { - char kbuf[BUFSZ]; - - You("wield the %s in your bare %s.", - corpse_xname(uswapwep, TRUE), body_part(HAND)); - - Strcpy(kbuf, an(corpse_xname(uswapwep, TRUE))); - instapetrify(kbuf); - uswapwepgone(); /* lifesaved still doesn't allow touching cockatrice */ - } + /* KMH -- ...or your secondary weapon when you're wielding it + [This case can't actually happen; twoweapon mode won't + engage if a corpse has been set up as the alternate weapon.] */ + if (u.twoweap && uswapwep && uswapwep->otyp == CORPSE) + wielding_corpse(uswapwep, on_purpose); return 0; } @@ -2127,7 +2139,8 @@ register struct obj *atmp; register struct obj *otmp; #define DESTROY_ARM(o) ((otmp = (o)) != 0 && \ (!atmp || atmp == otmp) && \ - (!obj_resists(otmp, 0, 90))) + (!obj_resists(otmp, 0, 90)) ? \ + (otmp->in_use = TRUE) : FALSE) if (DESTROY_ARM(uarmc)) { if (donning(otmp)) cancel_don(); diff --git a/src/read.c b/src/read.c index cc6069ea7..008eea41c 100644 --- a/src/read.c +++ b/src/read.c @@ -729,19 +729,23 @@ struct obj *sobj; /* KMH -- catch underflow */ s = sobj->cursed ? -otmp->spe : otmp->spe; if (s > (special_armor ? 5 : 3) && rn2(s)) { - pline("%s violently %s%s%s for a while, then %s.", - Yname2(otmp), - otense(otmp, Blind ? "vibrate" : "glow"), - (!Blind && !same_color) ? " " : nul, - (Blind || same_color) ? nul : - hcolor(sobj->cursed ? NH_BLACK : NH_SILVER), - otense(otmp, "evaporate")); + otmp->in_use = TRUE; + pline("%s violently %s%s%s for a while, then %s.", + Yname2(otmp), + otense(otmp, Blind ? "vibrate" : "glow"), + (!Blind && !same_color) ? " " : nul, + (Blind || same_color) ? nul : + hcolor(sobj->cursed ? NH_BLACK : NH_SILVER), + otense(otmp, "evaporate")); if(is_cloak(otmp)) (void) Cloak_off(); if(is_boots(otmp)) (void) Boots_off(); if(is_helmet(otmp)) (void) Helmet_off(); if(is_gloves(otmp)) (void) Gloves_off(); if(is_shield(otmp)) (void) Shield_off(); if(otmp == uarm) (void) Armor_gone(); +#ifdef TOURIST + if (is_shirt(otmp)) (void) Shirt_off(); +#endif useup(otmp); break; } diff --git a/src/trap.c b/src/trap.c index b3c530a23..ec60c7fca 100644 --- a/src/trap.c +++ b/src/trap.c @@ -4095,17 +4095,23 @@ boolean lava_effects() { register struct obj *obj, *obj2; - int dmg; + int dmg = d(6, 6); /* only applicable for water walking */ boolean usurvive; + usurvive = Fire_resistance || (Wwalking && dmg < u.uhp); + /* a timely interrupt might manage to salvage your life + but not your gear; do this before messages */ + if (!usurvive) + for (obj = invent; obj; obj = obj->nobj) + if (is_organic(obj) && !obj->oerodeproof) obj->in_use = TRUE; + burn_away_slime(); if (likes_lava(youmonst.data)) return FALSE; if (!Fire_resistance) { if(Wwalking) { - dmg = d(6,6); pline_The("lava here burns you!"); - if(dmg < u.uhp) { + if (usurvive) { losehp(dmg, lava_killer, KILLED_BY); /* lava damage */ goto burn_stuff; } @@ -4116,6 +4122,13 @@ lava_effects() #ifdef WIZARD if (wizard) usurvive = TRUE; #endif + + /* prevent Boots_off() -> spoteffects() -> lava_effects() recursion + which would successfully delete (via useupall) the no-longer-worn + boots; once recursive call returned, we would try to delete them + again here in the outer call (access stale memory, probably panic) */ + context.in_lava_effects++; + for(obj = invent; obj; obj = obj2) { obj2 = obj->nobj; if(is_organic(obj) && !obj->oerodeproof) { @@ -4130,7 +4143,7 @@ lava_effects() else if(obj == uarmg) (void) Gloves_off(); else if(obj == uarmf) (void) Boots_off(); #ifdef TOURIST - else if(obj == uarmu) setnotworn(obj); + else if(obj == uarmu) (void) Shirt_off(); #endif else if(obj == uleft) Ring_gone(obj); else if(obj == uright) Ring_gone(obj); @@ -4144,6 +4157,8 @@ lava_effects() } } + context.in_lava_effects--; + /* s/he died... */ u.uhp = -1; killer.format = KILLED_BY; -- 2.40.0