From 0447a1f107d516ff042301fd82ca378b48a46db1 Mon Sep 17 00:00:00 2001 From: PatR Date: Sun, 7 Jul 2024 16:43:55 -0700 Subject: [PATCH] band-aid for fuzzer crash in doclassdisco() This should prevent the unexplained situation in doclassdisco(the back-tick command) from triggering a crash, but doesn't solve the underlying problem. And the new impossible() will be escalated to panic() by the fuzzer, so will still cause it to die. Still no idea why the class input letter 'c' ended up with value 'I', leading to 'oclass' being MAXOCLASSES and going out of array bounds during during doclassdisco()'s final loop. --- include/decl.h | 2 +- src/o_init.c | 18 +++++++++++++----- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/include/decl.h b/include/decl.h index ecba32b1f..db8283ae8 100644 --- a/include/decl.h +++ b/include/decl.h @@ -194,7 +194,7 @@ struct instance_globals_b { #endif /* decl.c */ - int bases[MAXOCLASSES + 1]; + int bases[MAXOCLASSES + 2]; /* make bases[MAXOCLASSES+1] available */ coord bhitpos; /* place where throw or zap hits or stops */ struct obj *billobjs; /* objects not yet paid for */ diff --git a/src/o_init.c b/src/o_init.c index 7ae3840e7..5eb8ec865 100644 --- a/src/o_init.c +++ b/src/o_init.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 o_init.c $NHDT-Date: 1701720461 2023/12/04 20:07:41 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.79 $ */ +/* NetHack 3.7 o_init.c $NHDT-Date: 1720391455 2024/07/07 22:30:55 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.87 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2011. */ /* NetHack may be freely redistributed. See license for details. */ @@ -192,8 +192,12 @@ init_objects(void) /* extra entry allows deriving the range of a class via bases[class] through bases[class+1]-1 for all classes (except for ILLOBJ_CLASS which is separated from WEAPON_CLASS - by generic objects) */ - gb.bases[MAXOCLASSES] = NUM_OBJECTS; + by generic objects); second extra entry is to prevent an + explained crash in doclassdisco(), where the code ended up + attempting to process non-existent class MAXOCLASSES; the + [MAXOCLASSES+1] element gives that non-class 0 objects + when traversing objects[] from bases[X] through bases[X+1]-1 */ + gb.bases[MAXOCLASSES] = gb.bases[MAXOCLASSES + 1] = NUM_OBJECTS; /* hypothetically someone might remove all objects of some class, or be adding a new class and not populated it yet, leaving gaps in bases[]; guarantee that there are no such gaps */ @@ -757,7 +761,8 @@ dodiscovered(void) /* free after Robert Viduya */ if (oclass != prev_class) { if ((alphabyclass || lootsort) && sorted_ct) { /* output previous class */ - disco_output_sorted(tmpwin, sorted_lines, sorted_ct, lootsort); + disco_output_sorted(tmpwin, sorted_lines, sorted_ct, + lootsort); sorted_ct = 0; } if (!alphabetized || alphabyclass) { @@ -884,7 +889,7 @@ doclassdisco(void) i < NUM_OBJECTS && objects[i].oc_class == oclass; ++i) if ((dis = gd.disco[i]) != 0 && interesting_to_discover(dis)) { if (!strchr(discosyms, c)) { - Sprintf(eos(discosyms), "%c", c); + (void) strkitten(discosyms, c); if (!traditional) { any.a_int = c; add_menu(tmpwin, &nul_glyphinfo, &any, @@ -981,6 +986,9 @@ doclassdisco(void) break; default: oclass = def_char_to_objclass(c); + /* this should never happen but has been observed via the fuzzer */ + if (oclass == MAXOCLASSES) + impossible("doclassdisco: invalid object class '%s'", visctrl(c)); Sprintf(buf, "Discovered %s in %s", let_to_name(oclass, FALSE, FALSE), (flags.discosort == 'o') ? "order of discovery" : (flags.discosort == 's') ? "'sortloot' order"