give: Fix integer overflows on silly third arguments

give() silently caps the resulting number of items to 0..ITEM_MAX.

However, its test for "< 0" suffers integer overflow on two's
complement machines (i.e. practically everywhere) when the amount
argument is INT_MIN.  give() proceeds as if the result was in range:
it sets the number of items to (short)(n + INT_MIN), telexes the owner
that INT_MIN items were stolen (obviously bogus), and tells the deity
that there are now n + INT_MIN items in X,Y.

On common machines, (short)(n + INT_MIN) == n, i.e. nothing is given.

On an oddball machine with short as wide as int, the cast to short
does nothing, item_prewrite() oopses, and corrects the number of items
to zero.

In both cases, output and telegram lie.

Likewise, its test for "> ITEM_MAX" suffers integer overflow for
sufficiently big amount arguments.  Again, give() proceeds as if the
result was in range: it sets the number of items to (short)(n + amt),
telexes the owner that -amt items were stolen (obviously bogus), and
tells the deity that there are now close to INT_MIN items in X,Y.

On common machines, (short)(n + amt) = n + INT_MAX - amt - 1,
i.e. some items are stolen.

On an oddball machine with short as wide as int, the cast to short
does nothing, item_prewrite() oopses, and corrects the number of items
to zero.

Again, output and telegram lie.

Aside: setsector can suffer similar overflows, but it reports the
resulting change correctly.  Good enough.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
This commit is contained in:
Markus Armbruster 2013-01-20 16:39:32 +01:00
parent 546e1220e0
commit 9aaf609359
3 changed files with 10 additions and 10 deletions

View file

@ -29,6 +29,7 @@
* Known contributors to this file:
* David Muir Sharnoff
* Steve McClure, 1997
* Markus Armbruster, 2004-2013
*/
#include <config.h>
@ -65,11 +66,11 @@ give(void)
if (!check_sect_ok(&sect))
return RET_FAIL;
n = sect.sct_item[ip->i_uid];
if (amt < 0 && -amt > n) {
if (amt < 0 && n + amt < 0)
m = 0;
} else if (amt > 0 && amt + n > ITEM_MAX) {
else if (amt > 0 && n > ITEM_MAX - amt)
m = ITEM_MAX;
} else
else
m = n + amt;
sect.sct_item[ip->i_uid] = m;
putsect(&sect);