The static analyzer complained about use of 'obj' maybe being Null
when used in an impossible warning, but that warning will never
appear for the case where obj is actually Null. Add an assert()
that should let it figure that out, and move the impossible check
inside the 'else' clause where the check matters. (Either of those
by itself ought to be adequate to pacify the analyzer.)
Define some macros in include/tradstdc.h, for compilers that support
__attribute__((nonnull)), to assist in identifying which parameters
on functions are not supposed to be null pointers.
Next, for the majority of functions declared in include/extern.h, this
adds the appropriate macro that matches the actual use of each function's
parameters. The additions were done after performing some analysis.
These were the rules that were followed when determining which function
parameters should be nonnul, and which are nullable:
1. If the first use of, or reference to, the pointer parameter in the
function is a dereference, then the parameter will be considered
nonnull.
2. If there is code in the function that tests for the pointer parameter
being null, and adjusts the code-path accordingly so that no segfault
will occur, then the parameter will not be considered nonnull (it can
be null).
The use of the nonnull attributes allows the compiler to detect code in
callers of the function where a null parameter could get passed to the function.
If a warning is received the developer will have to do one of the following:
- If the null being passed to the function is now appropriate,
and the function should be able to expect a null parameter, then the
NONNULLxxx macro will have to be removed from the function's prototype.
or
- If the null being passed to the function is not appropriate,
correct the caller so it is not passing null.
or
- If the warning is about comparing to null, it may indicate an
unnecessary null check in the code involved. If it is deemed to be
unnecessary, it can then be removed.
Some static analysis tools apparently can work with the attribute, as well.
Following this, it was discovered that some functions were using one of the
(now) nonnull parameters in the first argument to the 'is_art(obj, ART)'
macro, which is defined like so:
=> #define is_art(o,art) ((o) && (o)->oartifact == (art))
That macro expansion inline resulted in a diagnostic warning because of the
'(o)' portion of the expanded macro, anywhere the macro was used with one of
the nonnull parameters. A test against null for a 'nonnull parameter' causes
a diagnostic warning.
To work around that, I replaced the is_art() macro with a function in
artifact.c, that accomplishes the same thing as the macro.
=> boolean
is_art(struct obj *obj, int art)
{
if (obj && obj->oartifact == art)
return TRUE;
return FALSE;
}
Some documentation...
These are the macros that have been defined for use when specifying the nonnull
parameters in a function prototype:
----------------------------------------------------------------------------
| Macro | Purpose |
+----------------+---------------------------------------------------------+
| NONULL | The function return value is never NULL. |
+----------------+---------------------------------------------------------+
| NONNULLPTRS | Every pointer argument is declared nonnull. |
+----------------+---------------------------------------------------------+
| NONNULLARG1 | The 1st argument is declared nonnull. |
+----------------+---------------------------------------------------------+
| NONNULLARG2 | The 2nd argument is declared nonnull. |
+----------------+---------------------------------------------------------+
| NONNULLARG3 | The 3rd argument is declared nonnull. |
+----------------+---------------------------------------------------------+
| NONNULLARG4 | The 4th argument is declared nonnull (not used). |
+----------------+---------------------------------------------------------+
| NONNULLARG5 | The 5th argument is declared nonnull. |
+----------------+---------------------------------------------------------+
| NONNULLARG7 | The 7th argument is declared nonnull (bhit). |
+----------------+---------------------------------------------------------+
| NONNULLARG12 | The 1st and 2nd arguments are declared nonnull. |
+----------------+---------------------------------------------------------+
| NONNULLARG13 | The 1st and 3rd arguments are declared nonnull. |
+----------------+---------------------------------------------------------+
| NONNULLARG123 | The 1st, 2nd and 3rd arguments are declared nonnull. |
+----------------+---------------------------------------------------------+
| NONNULLARG14 | The 1st and 4th arguments are declared nonnull. |
+----------------+---------------------------------------------------------+
| NONNULLARG134 | The 1st, 3rd and 4th arguments are declared nonnull. |
+----------------+---------------------------------------------------------+
| NONNULLARG17 | The 1st and 7th arguments are declared nonnull (this |
| | was a special-case added for askchain(), where the |
| | arguments are spread out that way. This macro |
| | could be removed if the askchain arguments in the |
| | prototype and callers were changed to make the |
| | nonnull arguments side-by-side). |
+----------------+---------------------------------------------------------+
| NONNULLARG145 | The 1st, 4th and 5th arguments are declared nonnull |
| | (this was a special-case added for find_roll_to_hit(), |
| | in uhitm.c, where the arguments are spread out that way.|
| | We can't just use NONNULLPTRS there because the 3rd |
| | argument 'weapon' can be NULL). |
+----------------+---------------------------------------------------------+
| NONNULLARG24 | The 2nd and 4th arguments are declared nonnull (this |
| | was a special-case added for query_objlist() |
| | in invent.c). |
+----------------+---------------------------------------------------------+
| NONNULLARG45 | The 4th and 5th arguments are declared nonnull (this |
| | was a special-case added for do_screen_description(), |
| | in pager.c, where the arguments are spread out that |
| | way. We can't just use NONNULLPTRS there because the |
| | 6th argument can be NULL). |
+----------------+---------------------------------------------------------+
| NO_NONNULLS | This macro expands to nothing. It is just used to |
| | mark that analysis has been done on the function, |
| | and concluded that none of the arguments could be |
| | marked nonnull.That distinguishes a function that has |
| | not been analyzed (yet), from one that has. |
+----------------+---------------------------------------------------------+
The NO_NONNULLS macro is meant to place a flag on the prototype to
make people aware that an assessed function was determined to not
be eligible for nonnull parameters. It expands to nothing.
Unfortunately, that macro was added partway through this exercise, so there
aren't many instances of it in the upper parts of include/extern.h, even though
the functions there were likely assessed and categorized as not having any
eligible nonnull parameters. It just never got any macro at all, in that case.
Following the parameter usage analysis that was done, the following was
noted:
Some NetHack functions have added a test to catch a passed null
parameter, and exit the function early as a result, or call
impossible(), and then exit. While that approach prevents segfaults
from dereferencing a null parameter, the early return is silent
(when impossible is not called anyway), and the function's true
purpose is not fulfilled. Also, the calling function may have no
awareness that the function did not complete its intended purpose,
in many instances.
Functions with such a test and early return, cannot have the parameter
declared 'nonnull', because the code to test for 'null' will cause a
diagnostic to be issued if the parameter is nonnull.
It might be good to revisit some of those functions and consider,
on a case by case basis, declaring the parameter nonnull in the
prototype, and the test/code-path commented out.
If a monster picks up an item thrown by the hero, change its 'how_lost'
flag from LOST_THROWN to LOST_STOLEN and pickup_stolen will be used
instead of pickup_thrown to decide whether to override pickup_types
and autopickup exceptions when hero eventually steps on it. If a
monster picks up an item dropped by the hero, change its flag to
LOST_NONE and autopickup will work normally without being overridden
by dropped_nopick. If item is flagged as stolen, leave it that way.
Moving over at item that's resting on ice gives a message about there
being ice present and then about the item, whether mention_decor is On
or Off. With it On, you'll get a message about being back on solid
ground as soon as you leave the ice. With it Off you wouldn't get
that at all if not levitating; that's the basic no-mention_decor
behavior for ice. However, if you were levitating, you would get a
delayed "back on solid ground" message when moving over some other
object, which might occur quite a bit later. Autopickup handling is
calling describe_decor() when the hero is levitating and some of that
wasn't appropriate for no-mention_decor.
This issue has been present since I first implemented mention_decor,
not introduced by recent back_on_ground() changes.
Fuzzer encountered an error because the game couldn't place
a level region; allow placing the stairs to the left or right
edge of the fixed map part, just in case the randomly generated
map didn't extend out of it.
Fix the teleport region preventing falling into the orctown.
selection_getbounds() has a check and early return.
Initialization will ensure a known state if that early return
were ever taken.
This is an alternative approach to pr #1163.
Reverse part of commit 8b5e9eadb1.
Shouldn't use get_obj_location(obj,...) to try to figure out if obj
has been deleted. That routine uses obj->where rather than scanning
the various object lists and doing that when obj has been deleted
would access stale memory.
The following were listed in extern.h as residing in makemon.c,
but they are actually in mon.c:
copy_mextra(struct monst *, struct monst *);
dealloc_mextra(struct monst *);
usmellmon(struct permonst *);
The new "riding blocks stealth" code was reading u.usteed at an
early point in the restore process, before its value was reliable
(dorecover()->restgamestate()->set_uasmon()->steed_vs_stealth());
because restgamestate() happens prior to restlevelstate(), the
value of u.usteed will be a stale pointer from some previous game,
and attempting to determine whether the steed is flying will crash
the game.
steed_vs_stealth() doesn't actually need to be called during the
restore process (because BStealth is saved in the save file), so
this can be easily fixed by omitting the call to it during the
restore.
A zap from a wand of striking shouldn't hit a drawbridge if it is
just passing over the span's spot when the bridge is closed. This
change matches the 3.7 behavior when the zap is performed by the hero.
It does hit if passing over that same spot when the bridge is open or
if it hits the portcullis spot whether open or closed.
Also, simplify the handling for the wand's destruction. It still
ends the zap early if that happens even though logically the rest of
the zap range should still be targetted.
After a music.c change earlier today, an annoying compiler warning resulted.
.\music.c(721): warning C4295: 'notes': array is too small to include
a terminating null character
Report was for an impossible "What an interesting effect (%d)" which
the fuzzer turned into a panic. Monster on the drawbridge zapped
toward the open portcullis, destroying the bridge and killing itself.
Wand was made of wood and burned up in lava under the fallen span.
Freeing the object zeroed it rather than leaving stale data, and the
zap continued while referencing freed memory that looked like it had
type STRANGE_OBJECT, triggering the impossible.
This will make a monster-induced zap stop early if a drawbridge
incident destroys the wand. That isn't the best possible fix
because the zap should continue despite the wand's destruction, but
at least it will now avoid triggering "intestsing effect".
When trying to reproduce the wand of striking "interesting effect (0)"
report, I tried wishing for lava under the castle drawbridge. That
wasn't handling drawbridges properly. This fixes wishing for moat,
lava, ice, or floor at a drawbridge span location whether the bridge
is currently open of closed. It also allows wishing for room or floor
or ground at room spots; that hasn't had much testing.
Wishing for furniture, pool|moat|water, or lava at an ice location
wasn't cancelling any pending melt timer.
ice_descr() was declared as returning const but returns its non-const
output buffer argument. Change to 'char *' so that wizterrainwish()
can capitilize that output without jumping through any hoops.
After the drawbridge was destroyed, playing an instrument on the castle
level while knowing the tune continued to offer a chance to play it.
Then nothing interesting happened even if you were close enough to the
former bridge for it to have been useful prior to the destruction.
I think the hero could also be given the tune as a divine prayer boon
after bridge destruction but I didn't verify that. The player might
not know that the tune is no good anymore, but the hero's patron deity
should.
Hide some of the details about new Stealth.
Streamline mon_break_armor(). Move replicated bypass handling into
m_lose_armor(). Also, eliminate a 'goto'.
Donning elven boots while riding and not already stealthy, you'd get
the message "you walk quietly" when not walking at all. Instead of
just changing the message, make riding a non-flying steed block
stealth. Riding a flying steed (or one you take aloft with an amulet
of flying) does not. It would have been quite a bit simpler to have
made riding anything block stealth, but the hard part is done.
The new formula is: (xlevel + Con)% chance of regenerating 1 hp
each turn.
This formula has been extensively playtested throughout the whole
game (including two ascensions). The intention is to make late-
game combat more interesting: early game the HP regeneration rate
is potentially slightly faster but not significantly changed, but
in the midgame and lategame is substantially slower because there
is no longer a big regeneration boost once the character's xlevel
is in the double digits.
With the new formula, I'm finding that my characters have to heal
with potions (rather than by waiting) in places that they never
had to before (e.g. lower Dungeons, and upper Gehennom), which in
turn means that fighting efficiently is now more important than it
was before. (In fact, in one of the games I wished for potions of
full healing on Astral for safety, although I think I would still
have won without.) It's also generally the case that you can no
longer regenerate "mid-fight": you need to disengage in order to
heal up. This made the game more fun as it meant that escape items
became more relevant, and I was using a greater range of items
throughout the game than I normally would.
The ring of regeneration has also been slightly buffed: it now
heals an extra 1hp per turn unconditionally (rather than becoming
less effective as the character levels). In both my test
ascensions, I found a ring of regeneration, but intentionally
refrained from using it in order to ensure that the new HP
regeneration rate would be tolerable even without one.
Issue reported by mkuoppal: drum of earthquake triggers a sanity
check warning which the fuzzer escalated to panic.
Analysis by entrez. If a pit gets created next to a pool or moat or
lava, liquid might flow there and replace the pit. But the drum of
earthquake code assumed that the pit was still there. If there was a
monster there and it wasn't levitating or flying and it wasn't killed
by the liquid, it got marked as trapped even though the pit was gone.
'sanity_check' noticed.
With difficulty I was able to reproduce the impossible warning before
the fix, but the are a bunch of random factors at play. After the
fix I can't reproduce it again, but that's not a guarantee that it's
actually fixed. The analysis seems correct though and the fix is
based on dealing with that.
Closes#1170
In the rare event that
make fetch-lua
is not working because the primary Lua site is not available,
provide another target
make fetch-lua-mirror
that uses the official lua mirror site documented at:
https://www.lua.org/mirrors.html
Allow 'm p' to pay via menu when menustyle is traditional and to pay
via the old sequence when it's combination, full, or partial. Also
revise the "Itemized billing?" prompt to accept 'm' as well as 'ynq'.
Answering 'm' will switch from the old sequence to the menu (whether
you got to that prompt via m-less 'p' for traditional or 'm p' for
other styles).
... and have more than 1 billed item, and using non-traditional
menustyle.
I opted to add an extra field to the bill struct, because
that made the code cleaner.
Breaks saves and bones.
During very early startup, Windows may not have loaded
the tty window procs yet, and it is running with safeprocs.
It will eventually load the tty stuff. If the currently
operating window port fails in can_set_perm_invent(),
try the check for WC_PERM_INVENT again explicitly on
the tty windowport.
There is a slight change in behavior here when collecting build-time
option information. If MAXOPT is too small, collecting will now save
MAXOPT lines and then skip anything that should come after. It used
to always keep the very last line, each time replacing whatever the
previous last line was, but would leak memory if that ever happened.
And after yesterday's "fix" it would try to keep that very last line
beyond the array bounds.