Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 22 Mar 2019 06:01:21 +1100 (EST)
From:      Bruce Evans <brde@optusnet.com.au>
To:        Conrad Meyer <cem@freebsd.org>
Cc:        src-committers@freebsd.org, svn-src-all@freebsd.org,  svn-src-head@freebsd.org
Subject:   Re: svn commit: r345380 - head/lib/libc/gen
Message-ID:  <20190322050731.V3169@besplex.bde.org>
In-Reply-To: <201903211445.x2LEj8U0002125@repo.freebsd.org>
References:  <201903211445.x2LEj8U0002125@repo.freebsd.org>

next in thread | previous in thread | raw e-mail | index | archive | help
On Thu, 21 Mar 2019, Conrad Meyer wrote:

> Log:
>  arc4random: Adjust example code to use uniform() API

This changes the example from correct to broken.

I see 4 bugs:

1. The log message says that the example uses uniform(), but it actually
    uses arc4random_uniform().

>  PR:		236678
>  Reported by:	Andras Farkas <deepbluemistake AT gmail.com>
>
> Modified:
>  head/lib/libc/gen/arc4random.3
>
> Modified: head/lib/libc/gen/arc4random.3
> ==============================================================================
> --- head/lib/libc/gen/arc4random.3	Thu Mar 21 14:17:55 2019	(r345379)
> +++ head/lib/libc/gen/arc4random.3	Thu Mar 21 14:45:08 2019	(r345380)
> @@ -31,7 +31,7 @@
> .\" Manual page, using -mandoc macros
> .\" $FreeBSD$
> .\"
> -.Dd July 19, 2014
> +.Dd March 21, 2019
> .Dt ARC4RANDOM 3
> .Os
> .Sh NAME
> @@ -116,7 +116,7 @@ and
> functions using
> .Fn arc4random :

2. This description still says that the example uses arc4random(), but it
actually uses arc4random_uniform().

> .Pp
> -.Dl "#define foo4random() (arc4random() % ((unsigned)RAND_MAX + 1))"
> +.Dl "#define foo4random() (arc4random_uniform(RAND_MAX + 1))"
> .Sh SEE ALSO
> .Xr rand 3 ,
> .Xr rand48 3 ,

3. RAND_MAX is usually INT_MAX, so adding 1 to it gives undefined behavior.
    The previous version carefully converted RAND_MAX to unsigned to get
    defined behaviour.

The PR doesn't have this bug.  It gives the different bad example of using
(unsigned)RAND_MAX.  This doesn't overflow, but is bad in other ways.  Its
cast to unsigned is bogus without adding 1.  Without adding 1, the range is
unusual and the example is different.  When misemulating rand() using
random() it is a common bug to take the result modulo RAND_MAX.  This gives
the very non-uniform distribution of never returning RAND_MAX.

rand() is not specified to have a uniform distribution, but quality of
implementation requires this.  This was broken in 4.4BSD but is fixed in
FreeBSD.  The fix involves reducing RAND_MAX by 2 to get a uniform
distribution in the supported range.  The old version was obviously not
uniform since it took values mod 0x7fffffff instead of mod 0x7fffffffU + 1,
so it missed 1 value in the full 31-bit range.  The it adjusted an
intermediate value of 0, which makes it unclear what the final range is.

Oops.  Since RAND_MAX is now 2 smaller than INT_MAX, adding 1 to it doesn't
cause overflow.  So the badness in the current example is just a style bug.

4. Another (old) bug in the example is that it says that the #define for
    foo4random() produces a drop-in replacement for [both] rand() and random().
    The maximum value for random() is still 0x7fffffff, so the same #define
    doesn't work for both, and it only ever worked accidentally if
    RAND_MAX happens to be 0x7fffffff, but RAND_MAX has been 0x7ffffffd since
    before this example existed.

Bruce



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20190322050731.V3169>