]> granicus.if.org Git - nethack/commitdiff
tentative fix for #K3626 - segfault when swallowed
authorPatR <rankin@nethack.org>
Tue, 21 Jun 2022 19:52:29 +0000 (12:52 -0700)
committerPatR <rankin@nethack.org>
Tue, 21 Jun 2022 19:52:29 +0000 (12:52 -0700)
release_hold() checked for (Upolyd && sticks(g.youmonst.data)) before
checking for (u.uswallow) and it could set u.ustuck to Null while
u.uswallow remained set to 1.  dmove_core() was accessing u.ustuck->mx
and u.ustuck->my after that, resulting in a crash.

This fixes that particular case but there might be others that also
assume sticky poly'd hero should be handled before swallowed hero.
Being swallowed/engulfed needs to be handled first.

doc/fixes3-7-0.txt
src/botl.c
src/cmd.c
src/zap.c

index 81f06cce1318458a1bb24c4a3efa33d66cc8e753..c0240e8ce9959d234645bebb13c430db0bbca3ef 100644 (file)
@@ -928,6 +928,12 @@ ball and chain could be accessed after having been freed if bones were saved
 early post-3.4.3 tried to fix the "naming artifacts trick" which could be used
        to distinguish the type of some undiscovered items, but using a name
        that only matched an artifact after capitalization was exploitable
+the u.ustuck hierarchy is:  swallowed by ustuck, hero poly'd into sticky form
+       is holding ustuck even if ustuck is sticky, ustuck is holding hero;
+       but some code assumed that the first two cases were reversed and
+       could make formerly sticky pold'd hero clear ustuck, leaving hero
+       swallowed by nothing (u.uswallow==1 with u.ustuck==NULL); that could
+       cause a crash if u.ustuck got dereferenced
 
 
 Fixes to 3.7.0-x Problems that Were Exposed Via git Repository
index 5a79f6e3fa8d53739d7c7452918bba4f4210dd9b..5d97eff9710cd0ea0998e9d834314c3aca4d425a 100644 (file)
@@ -887,23 +887,30 @@ bot_via_windowport(void)
                                        && !condtests[bl_tethered].test);
     }
     condtests[bl_grab].test = condtests[bl_held].test
+#if 0
+        = condtests[bl_engulfed].test
+#endif
         = condtests[bl_holding].test = FALSE;
     if (u.ustuck) {
-        if (Upolyd && sticks(g.youmonst.data)) {
-            test_if_enabled(bl_holding) = TRUE;
-        } else {
-            test_if_enabled(bl_grab) = (u.ustuck->data->mlet == S_EEL);
+        /* it is possible for a hero in sticks() form to be swallowed,
+           so swallowed needs to be checked first; it is not possible for
+           a hero in sticks() form to be held--sticky hero does the holding
+           even if u.ustuck is also a holder */
+        if (u.uswallow) {
+            /* engulfed/swallowed isn't currently a tracked status condition;
+               "held" might look odd for it but seems better than blank */
 #if 0
-            test_if_enabled(bl_engulfed) = u.uswallow ? TRUE : FALSE;
-            test_if_enabled(bl_held) = (!condtests[bl_grab].test
-                                        && !condtests[bl_engulfed].test);
+            test_if_enabled(bl_engulfed) = TRUE;
 #else
-            test_if_enabled(bl_held) = (!condtests[bl_grab].test
-                /* engulfed/swallowed isn't currently a tracked condition
-                   and showing "held" when in that state looks a bit odd,
-                   so suppress "held" if swallowed */
-                                        && !u.uswallow);
+            test_if_enabled(bl_held) = TRUE;
 #endif
+        } else if (Upolyd && sticks(g.youmonst.data)) {
+            test_if_enabled(bl_holding) = TRUE;
+        } else {
+            /* grab == hero is held by sea monster and about to be drowned;
+               held == hero is held by something else and can't move away */
+            test_if_enabled(bl_grab) = (u.ustuck->data->mlet == S_EEL);
+            test_if_enabled(bl_held) = !condtests[bl_grab].test;
         }
     }
     condtests[bl_blind].test     = (Blind) ? TRUE : FALSE;
index 293c611e2f064c31d85f434a9b287393fad79db0..73d1751050e5d24c70b87005a7bfadb9ec6ab538 100644 (file)
--- a/src/cmd.c
+++ b/src/cmd.c
@@ -146,6 +146,7 @@ static void mon_chain(winid, const char *, struct monst *, boolean, long *,
                       long *);
 static void contained_stats(winid, const char *, long *, long *);
 static void misc_stats(winid, long *, long *);
+static void you_sanity_check(void);
 static boolean accept_menu_prefix(const struct ext_func_tab *);
 static void reset_cmd_vars(boolean);
 static void mcmd_addmenu(winid, int, const char *);
@@ -3630,10 +3631,25 @@ wiz_show_stats(void)
 
 RESTORE_WARNING_FORMAT_NONLITERAL
 
+static void
+you_sanity_check(void)
+{
+    if (u.uswallow && !u.ustuck) {
+        /* this probably ought to be panic() */
+        impossible("sanity_check: swallowed by nothing?");
+        display_nhwindow(WIN_MESSAGE, TRUE);
+        /* try to recover from whatever the problem is */
+        u.uswallow = 0;
+        u.uswldtim = 0;
+        docrt();
+    }
+    (void) check_invent_gold("invent");
+}
+
 void
 sanity_check(void)
 {
-    (void) check_invent_gold("invent");
+    you_sanity_check();
     obj_sanity_check();
     timer_sanity_check();
     mon_sanity_check();
index 52a0558e2c0492fc2e7bf6d945cbfc98d21a2eb1..095e852dc5402b147f5acf84f5b422bbfad8dade 100644 (file)
--- a/src/zap.c
+++ b/src/zap.c
@@ -489,13 +489,7 @@ release_hold(void)
 
     if (!mtmp) {
         impossible("release_hold when not held?");
-    } else if (sticks(g.youmonst.data)) {
-        /* order matters if 'holding' status condition is enabled;
-           set_ustuck() will set flag for botl update, You() pline will
-           trigger a status update with "UHold" removed */
-        set_ustuck((struct monst *) 0);
-        You("release %s.", mon_nam(mtmp));
-    } else if (u.uswallow) {
+    } else if (u.uswallow) { /* possible for sticky hero to be swallowed */
         if (is_animal(mtmp->data)) {
             if (!Blind)
                 pline("%s opens its mouth!", Monnam(mtmp));
@@ -504,6 +498,12 @@ release_hold(void)
         }
         /* gives "you get regurgitated" or "you get expelled from <mon>" */
         expels(mtmp, mtmp->data, TRUE);
+    } else if (sticks(g.youmonst.data)) {
+        /* order matters if 'holding' status condition is enabled;
+           set_ustuck() will set flag for botl update, You() pline will
+           trigger a status update with "UHold" removed */
+        set_ustuck((struct monst *) 0);
+        You("release %s.", mon_nam(mtmp));
     } else { /* held but not swallowed */
         char relbuf[BUFSZ];