So this is the final verison of the patch solving the following
problems:

>The program is broken in multiple ways: return value clamping, casting
>from double to uint32_t, wrong error checking for putchar, lack of
>warnings when compiling.

Now the program is more pedantic and complicated, but at least it does
what is says in the man page.

Ok, deraadt, tb?

Index: Makefile
===================================================================
RCS file: /cvs/src/games/random/Makefile,v
retrieving revision 1.2
diff -u -p -r1.2 Makefile
--- Makefile    21 Sep 1997 11:36:51 -0000      1.2
+++ Makefile    5 Aug 2022 18:59:11 -0000
@@ -2,5 +2,6 @@
 
 PROG=  random
 MAN=   random.6
+CFLAGS=-Wall -Wconversion
 
 .include <bsd.prog.mk>
Index: random.6
===================================================================
RCS file: /cvs/src/games/random/random.6,v
retrieving revision 1.7
diff -u -p -r1.7 random.6
--- random.6    31 May 2007 19:19:18 -0000      1.7
+++ random.6    5 Aug 2022 18:59:11 -0000
@@ -55,9 +55,7 @@ If the
 option is specified,
 .Nm
 does not read or write anything, and simply exits with a random
-exit value of 0 to
-.Ar denominator
-\&- 1, inclusive.
+exit value of 0 to 255, inclusive.
 .It Fl r
 The
 .Fl r
Index: random.c
===================================================================
RCS file: /cvs/src/games/random/random.c,v
retrieving revision 1.20
diff -u -p -r1.20 random.c
--- random.c    7 Mar 2016 12:07:56 -0000       1.20
+++ random.c    5 Aug 2022 18:59:11 -0000
@@ -35,11 +35,15 @@
 
 #include <err.h>
 #include <errno.h>
+#include <math.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
 
 __dead void usage(void);
+unsigned clz64(uint64_t x);
+uint64_t random64(void);
+double random_real(void);
 
 int
 main(int argc, char *argv[])
@@ -84,9 +88,12 @@ main(int argc, char *argv[])
                usage(); 
        }
 
-       /* Compute a random exit status between 0 and denom - 1. */
+       /* 
+        * Compute a random exit status between 0 and 255.
+        * Shells only consider the first 8 bits of the return code.
+        */
        if (random_exit)
-               return (arc4random_uniform(denom));
+               return (int)arc4random_uniform(256);
 
        /*
         * Act as a filter, randomly choosing lines of the standard input
@@ -101,18 +108,19 @@ main(int argc, char *argv[])
         * 0 (which has a 1 / denom chance of being true), we select the
         * line.
         */
-       selected = arc4random_uniform(denom) == 0;
+       selected = random_real() < 1 / denom;
        while ((ch = getchar()) != EOF) {
-               if (selected)
-                       (void)putchar(ch);
-               if (ch == '\n') {
-                       /* End of that line.  See if we got an error. */
-                       if (ferror(stdout))
-                               err(2, "stdout");
+               int retch;
 
-                       /* Now see if the next line is to be printed. */
-                       selected = arc4random_uniform(denom) == 0;
+               if (selected) {
+                       errno = 0;
+                       retch = putchar(ch);
+                       if (retch == EOF && errno)
+                               err(2, "putchar");
                }
+               if (ch == '\n')
+                       /* Now see if the next line is to be printed. */
+                       selected = random_real() < 1 / denom;
        }
        if (ferror(stdin))
                err(2, "stdin");
@@ -123,6 +131,128 @@ void
 usage(void)
 {
 
-       (void)fprintf(stderr, "usage: %s [-er] [denominator]\n", getprogname());
+       if (fprintf(stderr, "usage: %s [-er] [denominator]\n", getprogname()) < 
0)
+               err(2, "fprintf");
        exit(1);
 }
+
+unsigned
+clz64(uint64_t x)
+{
+       static const uint64_t mask[] = {
+               0xffffffff00000000, 0xffff0000, 0xff00, 0xf0, 0xc, 0x2,
+       };
+       static unsigned zeroes[] = {
+               32, 16, 8, 4, 2, 1,
+       };
+       unsigned clz = 0;
+       int i;
+
+       if (x == 0)
+               return 64;
+
+       for (i = 0; i < 6; i++) {
+               if ((x & mask[i]) == 0)
+                       clz += zeroes[i];
+               else
+                       x >>= zeroes[i];
+       }
+
+       return clz;
+}
+
+uint64_t
+random64(void)
+{
+       uint64_t r;
+
+       arc4random_buf(&r, sizeof(uint64_t));
+
+       return r;
+}
+
+/*
+ * random_real: Generate a stream of bits uniformly at random and
+ * interpret it as the fractional part of the binary expansion of a
+ * number in [0, 1], 0.00001010011111010100...; then round it.
+ */
+double
+random_real(void)
+{
+       int exponent = -64;
+       uint64_t significand;
+       unsigned shift;
+
+       /*
+        * Read zeros into the exponent until we hit a one; the rest
+        * will go into the significand.
+        */
+       while (__predict_false((significand = random64()) == 0)) {
+               exponent -= 64;
+               /*
+                * If the exponent falls below -1074 = emin + 1 - p,
+                * the exponent of the smallest subnormal, we are
+                * guaranteed the result will be rounded to zero.  This
+                * case is so unlikely it will happen in realistic
+                * terms only if random64 is broken.
+                */
+               if (__predict_false(exponent < -1074))
+                       return 0;
+       }
+
+       /*
+        * There is a 1 somewhere in significand, not necessarily in
+        * the most significant position.  If there are leading zeros,
+        * shift them into the exponent and refill the less-significant
+        * bits of the significand.  Can't predict one way or another
+        * whether there are leading zeros: there's a fifty-fifty
+        * chance, if random64 is uniformly distributed.
+        */
+       shift = clz64(significand);
+       if (shift != 0) {
+               exponent -= shift;
+               significand <<= shift;
+               significand |= (random64() >> (64 - shift));
+       }
+
+       /*
+        * Set the sticky bit, since there is almost surely another 1
+        * in the bit stream.  Otherwise, we might round what looks
+        * like a tie to even when, almost surely, were we to look
+        * further in the bit stream, there would be a 1 breaking the
+        * tie.
+        */
+       significand |= 1;
+
+       /*
+        * Finally, convert to double (rounding) and scale by
+        * 2^exponent.
+        */
+       return ldexp((double)significand, exponent);
+}
+
+/*-
+ * Copyright (c) 2014 Taylor R. Campbell
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */

Reply via email to