Running /usr/games/arithmetic with a range of INT_MAX trips a "can't
happen" check, because of a signed integer overflow.
        $ arithmetic -r 2147483647
        arithmetic: bug: inconsistent penalties.

The actual overflow happens before a call to getrandom:
        right = getrandom(rangemax + 1, op, 1);
        left = getrandom(rangemax + 1, op, 0);
        result = getrandom(rangemax + 1, op, 0);
        left = getrandom(rangemax + 1, op, 0);
        result = getrandom(rangemax + 1, op, 0);
getrandom receives INT_MAX+1 == INT_MIN. arc4random_uniform treats it as
a uint32_t; i.e., 0x80000000, but the result is cast back into a signed
int. The following check (value < maxval) always fails.

There is another overflow inside getrandom, one which depends on values
accumulated at runtime, namely the "penalty" counters that increase the
occurrence of numbers you've gotten wrong before:
        value = arc4random_uniform(maxval + penalty[op][operand]);
Every time you guess an answer wrong, the value for (op, operand) in the
penalty grows by 5. If you get enough answers wrong, eventually the sum
maxval + penalty[op][operand] will exceed 0x7fffffff, and there's a
chance that arc4random_uniform will return a value that overflows a
signed int. If you get even more answers wrong, then eventually the sum
will overflow even a uint32_t; but realistically you will probably run
into memory problems before that because each wrong answer also
allocates a node in a linked list.

Here is a patch that (I think) fixes all the above mentioned overflows
but the last, and turns the last into a signed overflow instead of an
unsigned overflow. It would take about 429 million ((1<<31) / 5) wrong
answers to provoke that last overflow. The patch just uses uint32_t for
the rangemax variable and penalty counters.

Index: arithmetic.c
===================================================================
RCS file: /cvs/src/games/arithmetic/arithmetic.c,v
retrieving revision 1.27
diff -u -p -u -r1.27 arithmetic.c
--- arithmetic.c        11 Sep 2016 14:21:17 -0000      1.27
+++ arithmetic.c        27 Dec 2018 04:14:20 -0000
@@ -70,7 +70,7 @@
 #include <time.h>
 #include <unistd.h>
 
-int    getrandom(int, int, int);
+int    getrandom(uint32_t, int, int);
 __dead void    intr(int);
 int    opnum(int);
 void   penalise(int, int, int);
@@ -82,7 +82,7 @@ const char keylist[] = "+-x/";
 const char defaultkeys[] = "+-";
 const char *keys = defaultkeys;
 int nkeys = sizeof(defaultkeys) - 1;
-int rangemax = 10;
+uint32_t rangemax = 10;
 int nright, nwrong;
 time_t qtime;
 #define        NQUESTS 20
@@ -115,7 +115,7 @@ main(int argc, char *argv[])
                        break;
                }
                case 'r':
-                       rangemax = strtonum(optarg, 1, INT_MAX, &errstr);
+                       rangemax = strtonum(optarg, 1, (1ULL<<31)-1, &errstr);
                        if (errstr)
                                errx(1, "invalid range, %s: %s", errstr, 
optarg);
                        break;
@@ -266,9 +266,10 @@ retry:
  * penalties themselves.
  */
 
-int penalty[sizeof(keylist) - 1][2];
+uint32_t penalty[sizeof(keylist) - 1][2];
 struct penalty {
-       int value, penalty;     /* Penalised value and its penalty. */
+       int value;              /* Penalised value. */
+       uint32_t penalty;       /* Its penalty. */
        struct penalty *next;
 } *penlist[sizeof(keylist) - 1][2];
 
@@ -300,9 +301,9 @@ penalise(int value, int op, int operand)
  * we find the corresponding value and return that, decreasing its penalty.
  */
 int
-getrandom(int maxval, int op, int operand)
+getrandom(uint32_t maxval, int op, int operand)
 {
-       int value;
+       uint32_t value;
        struct penalty **pp, *p;
 
        op = opnum(op);
@@ -313,7 +314,7 @@ getrandom(int maxval, int op, int operan
         * are positions to be located in the penalty list.
         */
        if (value < maxval)
-               return(value);
+               return((int)value);
        value -= maxval;
 
        /*

Reply via email to