saving/freeing ball and chain

Reported by entrez:  fix memory being accessed after having been
freed by trying to keep ball&chain data up to date when they're
processed by the save code.  This fix is a little more elaborate
than this suggested one.  I'm crossing my fingers on this one....
This commit is contained in:
PatR
2022-06-11 00:08:17 -07:00
parent 03c19762d6
commit 542ab25da1
2 changed files with 46 additions and 21 deletions

View File

@@ -1,4 +1,4 @@
HDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.945 $ $NHDT-Date: 1654886097 2022/06/10 18:34:57 $
HDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.946 $ $NHDT-Date: 1654931291 2022/06/11 07:08:11 $
General Fixes and Modified Features
-----------------------------------
@@ -924,6 +924,7 @@ be less specific when cause of death is "handling a <ring or wand>" that
"a silver wand" rather than "ring of searching" or "wand of locking"
adjust the row placement of copyright and early startup messages so that
aren't partially overwritten by prompts that follow
ball and chain could be accessed after having been freed if bones were saved
Fixes to 3.7.0-x Problems that Were Exposed Via git Repository

View File

@@ -1,4 +1,4 @@
/* NetHack 3.7 save.c $NHDT-Date: 1651298444 2022/04/30 06:00:44 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.186 $ */
/* NetHack 3.7 save.c $NHDT-Date: 1654931286 2022/06/11 07:08:06 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.187 $ */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/*-Copyright (c) Michael Allison, 2009. */
/* NetHack may be freely redistributed. See license for details. */
@@ -21,6 +21,7 @@ static void savelevl(NHFILE *,boolean);
static void savedamage(NHFILE *);
static void save_bubbles(NHFILE *, xchar);
static void save_stairs(NHFILE *);
static void save_bc(NHFILE *);
static void saveobj(NHFILE *,struct obj *);
static void saveobjchn(NHFILE *,struct obj **);
static void savemon(NHFILE *,struct monst *);
@@ -273,7 +274,6 @@ static void
savegamestate(NHFILE* nhfp)
{
unsigned long uid;
struct obj *bc_objs = (struct obj *)0;
g.program_state.saving++; /* caller should/did already set this... */
uid = (unsigned long) getuid();
@@ -304,22 +304,8 @@ savegamestate(NHFILE* nhfp)
pointers into invent (uwep, uarmg, uamul, &c) are set to Null too */
saveobjchn(nhfp, &g.invent);
/* save ball and chain if they are currently dangling free (i.e. not
on floor or in inventory); 'looseball' and 'loosechain' have been
set up in caller because ball and chain will be gone by now if on
floor, or ball gone if carried; caveat: uball and uchain pointers
will be non-Null but stale (point to freed memory) in those cases */
bc_objs = (struct obj *) 0;
if (g.loosechain) {
g.loosechain->nobj = bc_objs; /* uchain */
bc_objs = g.loosechain;
}
if (g.looseball) {
g.looseball->nobj = bc_objs;
bc_objs = g.looseball;
}
saveobjchn(nhfp, &bc_objs); /* frees objs in list, sets bc_objs to Null */
g.looseball = g.loosechain = (struct obj *) 0;
/* save ball and chain if they happen to be in an unusal state */
save_bc(nhfp);
saveobjchn(nhfp, &g.migrating_objs); /* frees objs and sets to Null */
savemonchn(nhfp, g.migrating_mons);
@@ -332,7 +318,8 @@ savegamestate(NHFILE* nhfp)
savelevchn(nhfp);
if (nhfp->structlevel) {
bwrite(nhfp->fd, (genericptr_t) &g.moves, sizeof g.moves);
bwrite(nhfp->fd, (genericptr_t) &g.quest_status, sizeof g.quest_status);
bwrite(nhfp->fd, (genericptr_t) &g.quest_status,
sizeof g.quest_status);
bwrite(nhfp->fd, (genericptr_t) g.spl_book,
sizeof (struct spell) * (MAXSPELL + 1));
}
@@ -445,6 +432,8 @@ savestateinlock(void)
g.ustuck_id = (u.ustuck ? u.ustuck->m_id : 0);
g.usteed_id = (u.usteed ? u.usteed->m_id : 0);
/* if ball and/or chain aren't on floor or in invent, keep a copy
of their pointers; not valid when on floor or in invent */
g.looseball = BALL_IN_MON ? uball : 0;
g.loosechain = CHAIN_IN_MON ? uchain : 0;
savegamestate(nhfp);
@@ -751,10 +740,40 @@ save_stairs(NHFILE* nhfp)
}
}
/* if ball and/or chain are loose, make an object chain for it/them and
save that separately from other objects */
static void
save_bc(NHFILE *nhfp)
{
struct obj *bc_objs = 0;
/* save ball and chain if they are currently dangling free (i.e. not
on floor or in inventory); 'looseball' and 'loosechain' have been
set up in caller because ball and chain will be gone by now if on
floor, or ball gone if carried */
if (g.loosechain) {
g.loosechain->nobj = bc_objs; /* uchain */
bc_objs = g.loosechain;
if (nhfp->mode & FREEING) {
setworn((struct obj *) 0, W_CHAIN); /* sets 'uchain' to Null */
g.loosechain = (struct obj *) 0;
}
}
if (g.looseball) {
g.looseball->nobj = bc_objs;
bc_objs = g.looseball;
if (nhfp->mode & FREEING) {
setworn((struct obj *) 0, W_BALL); /* sets 'uball' to Null */
g.looseball = (struct obj *) 0;
}
}
saveobjchn(nhfp, &bc_objs); /* frees objs in list, sets bc_objs to Null */
}
/* save one object;
caveat: this is only for perform_bwrite(); caller handles release_data() */
static void
saveobj(NHFILE* nhfp, struct obj* otmp)
saveobj(NHFILE *nhfp, struct obj *otmp)
{
int buflen, zerobuf = 0;
@@ -839,6 +858,11 @@ saveobjchn(NHFILE* nhfp, struct obj** obj_p)
otmp->leashmon = 0; /* mon->mleashed could still be set;
* unfortunately, we can't clear that
* until after the monster is saved */
/* clear 'uball' and 'uchain' pointers if resetting their mask;
could also do same for other worn items but don't need to */
if ((otmp->owornmask & (W_BALL | W_CHAIN)) != 0)
setworn((struct obj *) 0,
otmp->owornmask & (W_BALL | W_CHAIN));
otmp->owornmask = 0L; /* no longer care */
dealloc_obj(otmp);
}