NetBSD Problem Report #25367

Received: (qmail 14639 invoked by uid 605); 28 Apr 2004 15:31:34 -0000
Message-Id: <200404281531.i3SFV1BY009211@apb.cequrux.com>
Date: Wed, 28 Apr 2004 17:31:01 +0200 (SAST)
From: apb@cequrux.com
Sender: gnats-bugs-owner@NetBSD.org
To: gnats-bugs@gnats.NetBSD.org
Subject: arc4random state is shared across forks
X-Send-Pr-Version: 3.95

>Number:         25367
>Category:       lib
>Synopsis:       arc4random state is shared across forks
>Confidential:   no
>Severity:       serious
>Priority:       high
>Responsible:    lib-bug-people
>State:          closed
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Wed Apr 28 15:32:00 +0000 2004
>Closed-Date:    Mon Jul 21 08:21:46 +0000 2014
>Last-Modified:  Mon Jul 21 08:21:46 +0000 2014
>Originator:     Alan Barrett
>Release:        NetBSD 1.6ZK
>Organization:
	Not much
>Environment:
System: NetBSD 1.6ZK
Architecture: i386
Machine: i386
>Description:
arc4random() returns the same stream of values in all children of
a common parent process.  This violates the principle of least
astonishment.

>How-To-Repeat:
sh$ ./test_arc4random
top-level process 28045: toprand=0xb70ed1c5
child process 22848: childrand[0]=0x617d6e3e
child process 22848: childrand[1]=0xd9af1d32
child process 14579: childrand[0]=0x617d6e3e
child process 14579: childrand[1]=0xd9af1d32
child process 5365: childrand[0]=0x617d6e3e
child process 5365: childrand[1]=0xd9af1d32
child process 17748: childrand[0]=0x617d6e3e
child process 17748: childrand[1]=0xd9af1d32
child process 17560: childrand[0]=0x617d6e3e
child process 17560: childrand[1]=0xd9af1d32
sh$ cat ./test_arc4random.c
#include <unistd.h>
#include <stdlib.h>
#include <inttypes.h>
#include <sys/wait.h>

#define CHILDREN 5		/* number of children to run */
#define RAND_PER_CHILD 2	/* number of arc4random() calls per child */

int
main(int argc, char **argv)
{
    int nchildren = 0;
    int nerrors = 0;
    pid_t toppid = getpid();
    u_int32_t toprand;

    /* initialise arc4random()'s private data in the parent */
    toprand = arc4random();
    printf("top-level process %lu: toprand=0x%" PRIx32 "\n",
		(unsigned long)toppid, toprand);
    fflush(stdout);

    for (nerrors = nchildren = 0; nchildren < CHILDREN && nerrors <= 10; ) {
	pid_t forkresult = fork();
	if (forkresult < 0) {
	    nerrors++;
	    sleep(1);
	} else if (forkresult == 0) {
	    pid_t childpid = getpid();
	    u_int32_t childrand;
	    int i;
	    for (i = 0; i < RAND_PER_CHILD; i++) {
		childrand = arc4random();
		printf("child process %lu: childrand[%d]=0x%" PRIx32 "\n",
		    (unsigned long)childpid, i, childrand);
	    }
	    fflush(stdout);
	    exit(0);
	} else {
	    nchildren++;
	    (void) wait(NULL);
	}
    }
    exit(0);
}

>Fix:
Somehow make arc4random re-seed itself after a fork().

Since there seems to be no fork_hooks infrastructure, I suppose we could
save the pid somewhere and re-seed if getpid() says the pid has changed.

>Release-Note:
>Audit-Trail:
From: David Laight <david@l8s.co.uk>
To: gnats@netbsd.org
Cc: 
Subject: Re: lib/25367: arc4random state is shared across forks
Date: Sun, 19 Aug 2012 09:01:48 +0100

 Just bumped into this one while looking at other arc4random bugs.
 There are two freebsd threads from september 2008 explaining that
 openbsd had added a getpid() call prior to every arc4random() one
 in order to detect this - butthat is slow. See:
 http://lists.freebsd.org/pipermail/freebsd-current/2008-September/088552.html

 There is a more general problem of detecting fork() - arc4random()
 isn't the only affected code.

 Optimising getpid() (or having an optimised getpid() function) is one
 possibility. All that requires is for libc to zero the cached pid
 value in the child of fork().
 Although vfork() and others? may need special consideration.

 	David

 -- 
 David Laight: david@l8s.co.uk

From: David Holland <dholland-bugs@netbsd.org>
To: gnats-bugs@NetBSD.org
Cc: 
Subject: Re: lib/25367: arc4random state is shared across forks
Date: Wed, 22 Aug 2012 04:06:06 +0000

 On Sun, Aug 19, 2012 at 04:25:02PM +0000, David Laight wrote:
  >  Optimising getpid() (or having an optimised getpid() function) is one
  >  possibility. All that requires is for libc to zero the cached pid
  >  value in the child of fork().
  >  Although vfork() and others? may need special consideration.

 I think it's ok for a vfork child to share the arc4random state with
 its parent, because it'll be shared read/write rather than copied.

 -- 
 David A. Holland
 dholland@netbsd.org

From: Roy Marples <roy@marples.name>
To: gnats-bugs@netbsd.org
Cc: apb@cequrux.com, David Laight <david@l8s.co.uk>, David Holland
 <dholland-bugs@netbsd.org>
Subject: Re: lib/25367: arc4random state is shared across forks
Date: Sun, 25 May 2014 08:30:35 +0100

 --=_78a787e1cccc4ca0768aab6f423d85a8
 Content-Transfer-Encoding: 7bit
 Content-Type: text/plain; charset=UTF-8;
  format=flowed

 Hi

 I bumped into this as well.
 I looked over the FreeBSD thread regarding potential speed issues and 
 despite any concerns they have actually merged the change in.
 Attached is a patch to -current which does two things.

 1) Re-stirs if the pid changes
 2) Re-stirs after swallowing 1600000 bytes (which also addresses 
 PR/45952)

 I've been running this on my laptop for a while with great success and 
 unless anyone objects I'll commit it shortly.

 Thanks

 Roy

 --=_78a787e1cccc4ca0768aab6f423d85a8
 Content-Transfer-Encoding: base64
 Content-Type: text/x-diff;
  name=arc4random-pid.diff
 Content-Disposition: attachment;
  filename=arc4random-pid.diff;
  size=3981

 PyB0ZXN0Cj8gdGVzdC5jCkluZGV4OiBnZW4vYXJjNHJhbmRvbS5jCj09PT09PT09PT09PT09PT09
 PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KUkNTIGZp
 bGU6IC9jdnNyb290L3NyYy9saWIvbGliYy9nZW4vYXJjNHJhbmRvbS5jLHYKcmV0cmlldmluZyBy
 ZXZpc2lvbiAxLjIxCmRpZmYgLXUgLXAgLXIxLjIxIGFyYzRyYW5kb20uYwotLS0gZ2VuL2FyYzRy
 YW5kb20uYwkxNyBPY3QgMjAxMyAyMzo1NjoxNyAtMDAwMAkxLjIxCisrKyBnZW4vYXJjNHJhbmRv
 bS5jCTI0IE1heSAyMDE0IDIxOjQwOjEzIC0wMDAwCkBAIC00OSwxMSArNDksMTEgQEAgX193ZWFr
 X2FsaWFzKGFyYzRyYW5kb21fdW5pZm9ybSxfYXJjNHJhbgogI2VuZGlmCiAKIHN0cnVjdCBhcmM0
 X3N0cmVhbSB7Ci0JdWludDhfdCBzdGlycmVkOwotCXVpbnQ4X3QgcGFkOwogCXVpbnQ4X3QgaTsK
 IAl1aW50OF90IGo7CiAJdWludDhfdCBzWyh1aW50OF90KX4wdSArIDF1XTsJLyogMjU2IHRvIHlv
 dSBhbmQgbWUgKi8KKwlzaXplX3QgY291bnQ7CisJcGlkX3Qgc3Rpcl9waWQ7CiAJbXV0ZXhfdCBt
 dHg7CiB9OwogCkBAIC02Nyw3ICs2Nyw3IEBAIHN0cnVjdCBhcmM0X3N0cmVhbSB7CiAJCQltdXRl
 eF91bmxvY2soJihycyktPm10eCk7ICAgICAgXAogCX0KICNlbHNlCi0jZGVmaW5lIExPQ0socnMp
 IAorI2RlZmluZSBMT0NLKHJzKQogI2RlZmluZSBVTkxPQ0socnMpCiAjZW5kaWYKIApAQCAtNzgs
 NyArNzgsNyBAQCBzdHJ1Y3QgYXJjNF9zdHJlYW0gewogI2RlZmluZSBTMjU2IFM2NCgwKSwgUzY0
 KDY0KSwgUzY0KDEyOCksIFM2NCgxOTIpCiAKIHN0YXRpYyBzdHJ1Y3QgYXJjNF9zdHJlYW0gcnMg
 PSB7IC5pID0gMHhmZiwgLmogPSAwLCAucyA9IHsgUzI1NiB9LAotCQkuc3RpcnJlZCA9IDAsIC5t
 dHggPSBNVVRFWF9JTklUSUFMSVpFUiB9OworCQkuY291bnQgPSAwLCAuc3Rpcl9waWQgPSAwLCAu
 bXR4ID0gTVVURVhfSU5JVElBTElaRVIgfTsKIAogI3VuZGVmIFMKICN1bmRlZiBTNApAQCAtODcs
 MjAgKzg3LDEwIEBAIHN0YXRpYyBzdHJ1Y3QgYXJjNF9zdHJlYW0gcnMgPSB7IC5pID0gMHgKICN1
 bmRlZiBTMjU2CiAKIHN0YXRpYyBpbmxpbmUgdm9pZCBhcmM0X2FkZHJhbmRvbShzdHJ1Y3QgYXJj
 NF9zdHJlYW0gKiwgdV9jaGFyICosIGludCk7Ci1zdGF0aWMgX19ub2lubGluZSB2b2lkIGFyYzRf
 c3RpcihzdHJ1Y3QgYXJjNF9zdHJlYW0gKik7CitzdGF0aWMgX19ub2lubGluZSB2b2lkIGFyYzRf
 c3RpcihzdHJ1Y3QgYXJjNF9zdHJlYW0gKiwgcGlkX3QpOwogc3RhdGljIGlubGluZSB1aW50OF90
 IGFyYzRfZ2V0Ynl0ZShzdHJ1Y3QgYXJjNF9zdHJlYW0gKik7CiBzdGF0aWMgaW5saW5lIHVpbnQz
 Ml90IGFyYzRfZ2V0d29yZChzdHJ1Y3QgYXJjNF9zdHJlYW0gKik7CiAKLXN0YXRpYyBpbmxpbmUg
 aW50Ci1hcmM0X2NoZWNrX2luaXQoc3RydWN0IGFyYzRfc3RyZWFtICphcykKLXsKLQlpZiAoX19w
 cmVkaWN0X3RydWUocnMuc3RpcnJlZCkpCi0JCXJldHVybiAwOwotCi0JYXJjNF9zdGlyKGFzKTsK
 LQlyZXR1cm4gMTsKLX0KLQogc3RhdGljIGlubGluZSB2b2lkCiBhcmM0X2FkZHJhbmRvbShzdHJ1
 Y3QgYXJjNF9zdHJlYW0gKmFzLCB1X2NoYXIgKmRhdCwgaW50IGRhdGxlbikKIHsKQEAgLTExNyw3
 ICsxMDcsNyBAQCBhcmM0X2FkZHJhbmRvbShzdHJ1Y3QgYXJjNF9zdHJlYW0gKmFzLCB1CiB9CiAK
 IHN0YXRpYyBfX25vaW5saW5lIHZvaWQKLWFyYzRfc3RpcihzdHJ1Y3QgYXJjNF9zdHJlYW0gKmFz
 KQorYXJjNF9zdGlyKHN0cnVjdCBhcmM0X3N0cmVhbSAqYXMsIHBpZF90IHBpZCkKIHsKIAlpbnQg
 cmRhdFszMl07CiAJaW50IG1pYltdID0geyBDVExfS0VSTiwgS0VSTl9VUk5EIH07CkBAIC0xNDYs
 MTAgKzEzNiwyNCBAQCBhcmM0X3N0aXIoc3RydWN0IGFyYzRfc3RyZWFtICphcykKIAkgKiBwYXBl
 ciAiV2Vha25lc3NlcyBpbiB0aGUgS2V5IFNjaGVkdWxpbmcgQWxnb3JpdGhtIG9mIFJDNCIKIAkg
 KiBieSBGbHVoZXIsIE1hbnRpbiwgYW5kIFNoYW1pci4gIChOID0gMjU2IGluIG91ciBjYXNlLikK
 IAkgKi8KLQlmb3IgKGogPSAwOyBqIDwgX19hcnJheWNvdW50KGFzLT5zKSAqIDQ7IGorKykKKwlm
 b3IgKGogPSAwOyBqIDwgX19hcnJheWNvdW50KGFzLT5zKSAqIHNpemVvZih1aW50MzJfdCk7IGor
 KykKIAkJYXJjNF9nZXRieXRlKGFzKTsKIAotCWFzLT5zdGlycmVkID0gMTsKKwkvKiBTdGlyIGFn
 YWluIGFmdGVyIHN3YWxsb3dpbmcgMTYwMDAwMCBieXRlcyBvciBpZiB0aGUgcGlkIGNoYW5nZXMg
 Ki8KKwlhcy0+Y291bnQgPSAxNjAwMDAwOworCWFzLT5zdGlyX3BpZCA9IHBpZDsKK30KKworc3Rh
 dGljIGlubGluZSB2b2lkCithcmM0X3N0aXJfaWZfbmVlZGVkKHN0cnVjdCBhcmM0X3N0cmVhbSAq
 YXMsIHNpemVfdCBsZW4pCit7CisJcGlkX3QgcGlkOworCisJcGlkID0gZ2V0cGlkKCk7CisJaWYg
 KGFzLT5jb3VudCA8PSBsZW4gfHwgYXMtPnN0aXJfcGlkICE9IHBpZCkKKwkJYXJjNF9zdGlyKGFz
 LCBwaWQpOworCWVsc2UKKwkJYXMtPmNvdW50IC09IGxlbjsKIH0KIAogc3RhdGljIF9faW5saW5l
 IHVpbnQ4X3QKQEAgLTE2OSw2ICsxNzMsNyBAQCBhcmM0X2dldGJ5dGVfaWooc3RydWN0IGFyYzRf
 c3RyZWFtICphcywgCiBzdGF0aWMgaW5saW5lIHVpbnQ4X3QKIGFyYzRfZ2V0Ynl0ZShzdHJ1Y3Qg
 YXJjNF9zdHJlYW0gKmFzKQogeworCiAJcmV0dXJuIGFyYzRfZ2V0Ynl0ZV9paihhcywgJmFzLT5p
 LCAmYXMtPmopOwogfQogCkBAIC0xNzYsNiArMTgxLDcgQEAgc3RhdGljIGlubGluZSB1aW50MzJf
 dAogYXJjNF9nZXR3b3JkKHN0cnVjdCBhcmM0X3N0cmVhbSAqYXMpCiB7CiAJdWludDMyX3QgdmFs
 OworCiAJdmFsID0gYXJjNF9nZXRieXRlKGFzKSA8PCAyNDsKIAl2YWwgfD0gYXJjNF9nZXRieXRl
 KGFzKSA8PCAxNjsKIAl2YWwgfD0gYXJjNF9nZXRieXRlKGFzKSA8PCA4OwpAQCAtMTg2LDE2ICsx
 OTIsMTggQEAgYXJjNF9nZXR3b3JkKHN0cnVjdCBhcmM0X3N0cmVhbSAqYXMpCiB2b2lkCiBhcmM0
 cmFuZG9tX3N0aXIodm9pZCkKIHsKKwogCUxPQ0soJnJzKTsKLQlhcmM0X3N0aXIoJnJzKTsKKwlh
 cmM0X3N0aXIoJnJzLCBnZXRwaWQoKSk7CiAJVU5MT0NLKCZycyk7CiB9CiAKIHZvaWQKIGFyYzRy
 YW5kb21fYWRkcmFuZG9tKHVfY2hhciAqZGF0LCBpbnQgZGF0bGVuKQogeworCiAJTE9DSygmcnMp
 OwotCWFyYzRfY2hlY2tfaW5pdCgmcnMpOworCWFyYzRfc3Rpcl9pZl9uZWVkZWQoJnJzLCBkYXRs
 ZW4pOwogCWFyYzRfYWRkcmFuZG9tKCZycywgZGF0LCBkYXRsZW4pOwogCVVOTE9DSygmcnMpOwog
 fQpAQCAtMjA2LDcgKzIxNCw3IEBAIGFyYzRyYW5kb20odm9pZCkKIAl1aW50MzJfdCB2OwogCiAJ
 TE9DSygmcnMpOwotCWFyYzRfY2hlY2tfaW5pdCgmcnMpOworCWFyYzRfc3Rpcl9pZl9uZWVkZWQo
 JnJzLCBzaXplb2YodikpOwogCXYgPSBhcmM0X2dldHdvcmQoJnJzKTsKIAlVTkxPQ0soJnJzKTsK
 IAlyZXR1cm4gdjsKQEAgLTIyMCw3ICsyMjgsNyBAQCBhcmM0cmFuZG9tX2J1Zih2b2lkICpidWYs
 IHNpemVfdCBsZW4pCiAJdWludDhfdCBpLCBqOwogCiAJTE9DSygmcnMpOwotCWFyYzRfY2hlY2tf
 aW5pdCgmcnMpOworCWFyYzRfc3Rpcl9pZl9uZWVkZWQoJnJzLCBsZW4pOwogCiAJLyogY2FjaGUg
 aSBhbmQgaiAtIGNvbXBpbGVyIGNhbid0IGtub3cgJ2J1ZicgZG9lc24ndCBhbGlhcyB0aGVtICov
 CiAJaSA9IHJzLmk7CkBAIC0yNjMsNyArMjcxLDcgQEAgYXJjNHJhbmRvbV91bmlmb3JtKHVpbnQz
 Ml90IHVwcGVyX2JvdW5kKQogCW1pbiA9ICgweEZGRkZGRkZGVSAtIHVwcGVyX2JvdW5kICsgMSkg
 JSB1cHBlcl9ib3VuZDsKIAogCUxPQ0soJnJzKTsKLQlhcmM0X2NoZWNrX2luaXQoJnJzKTsKKwlh
 cmM0X3N0aXJfaWZfbmVlZGVkKCZycywgc2l6ZW9mKHIpKTsKIAogCS8qCiAJICogVGhpcyBjb3Vs
 ZCB0aGVvcmV0aWNhbGx5IGxvb3AgZm9yZXZlciBidXQgZWFjaCByZXRyeSBoYXMK
 --=_78a787e1cccc4ca0768aab6f423d85a8--

From: Joerg Sonnenberger <joerg@britannica.bec.de>
To: gnats-bugs@NetBSD.org
Cc: 
Subject: Re: lib/25367: arc4random state is shared across forks
Date: Sun, 25 May 2014 23:02:34 +0200

 This is wrong, it should just use pthread_atfork without penalizing the
 common case.

 Joerg

From: David Holland <dholland-bugs@netbsd.org>
To: gnats-bugs@netbsd.org
Cc: 
Subject: Re: lib/25367: arc4random state is shared across forks
Date: Fri, 30 May 2014 08:19:43 +0000

 here's another copy of the mail above (that got sent to the wrong
 place) in which the patch isn't base64'd:

    ------

 From: Roy Marples <roy@marples.name>
 To: gnats@netbsd.org
 Subject: Re: lib/25367: arc4random state is shared across forks
 Date: Sat, 24 May 2014 23:07:07 +0100

 Hi

 I bumped into this as well.
 I looked over the FreeBSD thread regarding potential speed issues and despite
 any concerns they have actually merged the change in.
 Attached is a patch to -current which does two things.

 1) Re-stirs if the pid changes
 2) Re-stirs after swallowing 1600000 bytes (which also addresses PR/45952)

 I've been running this on my laptop for a while with great success and unless
 anyone objects I'll commit it shortly.

 Thanks

 Roy

 ? test
 ? test.c
 Index: gen/arc4random.c
 ===================================================================
 RCS file: /cvsroot/src/lib/libc/gen/arc4random.c,v
 retrieving revision 1.21
 diff -u -p -r1.21 arc4random.c
 --- gen/arc4random.c	17 Oct 2013 23:56:17 -0000	1.21
 +++ gen/arc4random.c	24 May 2014 21:40:13 -0000
 @@ -49,11 +49,11 @@ __weak_alias(arc4random_uniform,_arc4ran
  #endif

  struct arc4_stream {
 -	uint8_t stirred;
 -	uint8_t pad;
  	uint8_t i;
  	uint8_t j;
  	uint8_t s[(uint8_t)~0u + 1u];	/* 256 to you and me */
 +	size_t count;
 +	pid_t stir_pid;
  	mutex_t mtx;
  };

 @@ -67,7 +67,7 @@ struct arc4_stream {
  			mutex_unlock(&(rs)->mtx);      \
  	}
  #else
 -#define LOCK(rs) 
 +#define LOCK(rs)
  #define UNLOCK(rs)
  #endif

 @@ -78,7 +78,7 @@ struct arc4_stream {
  #define S256 S64(0), S64(64), S64(128), S64(192)

  static struct arc4_stream rs = { .i = 0xff, .j = 0, .s = { S256 },
 -		.stirred = 0, .mtx = MUTEX_INITIALIZER };
 +		.count = 0, .stir_pid = 0, .mtx = MUTEX_INITIALIZER };

  #undef S
  #undef S4
 @@ -87,20 +87,10 @@ static struct arc4_stream rs = { .i = 0x
  #undef S256

  static inline void arc4_addrandom(struct arc4_stream *, u_char *, int);
 -static __noinline void arc4_stir(struct arc4_stream *);
 +static __noinline void arc4_stir(struct arc4_stream *, pid_t);
  static inline uint8_t arc4_getbyte(struct arc4_stream *);
  static inline uint32_t arc4_getword(struct arc4_stream *);

 -static inline int
 -arc4_check_init(struct arc4_stream *as)
 -{
 -	if (__predict_true(rs.stirred))
 -		return 0;
 -
 -	arc4_stir(as);
 -	return 1;
 -}
 -
  static inline void
  arc4_addrandom(struct arc4_stream *as, u_char *dat, int datlen)
  {
 @@ -117,7 +107,7 @@ arc4_addrandom(struct arc4_stream *as, u
  }

  static __noinline void
 -arc4_stir(struct arc4_stream *as)
 +arc4_stir(struct arc4_stream *as, pid_t pid)
  {
  	int rdat[32];
  	int mib[] = { CTL_KERN, KERN_URND };
 @@ -146,10 +136,24 @@ arc4_stir(struct arc4_stream *as)
  	 * paper "Weaknesses in the Key Scheduling Algorithm of RC4"
  	 * by Fluher, Mantin, and Shamir.  (N = 256 in our case.)
  	 */
 -	for (j = 0; j < __arraycount(as->s) * 4; j++)
 +	for (j = 0; j < __arraycount(as->s) * sizeof(uint32_t); j++)
  		arc4_getbyte(as);

 -	as->stirred = 1;
 +	/* Stir again after swallowing 1600000 bytes or if the pid changes */
 +	as->count = 1600000;
 +	as->stir_pid = pid;
 +}
 +
 +static inline void
 +arc4_stir_if_needed(struct arc4_stream *as, size_t len)
 +{
 +	pid_t pid;
 +
 +	pid = getpid();
 +	if (as->count <= len || as->stir_pid != pid)
 +		arc4_stir(as, pid);
 +	else
 +		as->count -= len;
  }

  static __inline uint8_t
 @@ -169,6 +173,7 @@ arc4_getbyte_ij(struct arc4_stream *as, 
  static inline uint8_t
  arc4_getbyte(struct arc4_stream *as)
  {
 +
  	return arc4_getbyte_ij(as, &as->i, &as->j);
  }

 @@ -176,6 +181,7 @@ static inline uint32_t
  arc4_getword(struct arc4_stream *as)
  {
  	uint32_t val;
 +
  	val = arc4_getbyte(as) << 24;
  	val |= arc4_getbyte(as) << 16;
  	val |= arc4_getbyte(as) << 8;
 @@ -186,16 +192,18 @@ arc4_getword(struct arc4_stream *as)
  void
  arc4random_stir(void)
  {
 +
  	LOCK(&rs);
 -	arc4_stir(&rs);
 +	arc4_stir(&rs, getpid());
  	UNLOCK(&rs);
  }

  void
  arc4random_addrandom(u_char *dat, int datlen)
  {
 +
  	LOCK(&rs);
 -	arc4_check_init(&rs);
 +	arc4_stir_if_needed(&rs, datlen);
  	arc4_addrandom(&rs, dat, datlen);
  	UNLOCK(&rs);
  }
 @@ -206,7 +214,7 @@ arc4random(void)
  	uint32_t v;

  	LOCK(&rs);
 -	arc4_check_init(&rs);
 +	arc4_stir_if_needed(&rs, sizeof(v));
  	v = arc4_getword(&rs);
  	UNLOCK(&rs);
  	return v;
 @@ -220,7 +228,7 @@ arc4random_buf(void *buf, size_t len)
  	uint8_t i, j;

  	LOCK(&rs);
 -	arc4_check_init(&rs);
 +	arc4_stir_if_needed(&rs, len);

  	/* cache i and j - compiler can't know 'buf' doesn't alias them */
  	i = rs.i;
 @@ -263,7 +271,7 @@ arc4random_uniform(uint32_t upper_bound)
  	min = (0xFFFFFFFFU - upper_bound + 1) % upper_bound;

  	LOCK(&rs);
 -	arc4_check_init(&rs);
 +	arc4_stir_if_needed(&rs, sizeof(r));

  	/*
  	 * This could theoretically loop forever but each retry has

From: Roy Marples <roy@marples.name>
To: gnats-bugs@netbsd.org
Cc: apb@cequrux.com, David Laight <david@l8s.co.uk>,
 dholland-bugs@netbsd.org, Joerg Sonnenberger <joerg@britannica.bec.de>
Subject: Re: lib/25367: arc4random state is shared across forks
Date: Sat, 31 May 2014 01:15:34 +0100

 Thanks Joerg!
 I learned a new function :)

 Posted inline (gnats sucks) is a new patch using pthread_atfork.
 However, our man page and online documentation I can find is sadly 
 lacking in a few places so I'm not entirely sure the patch is correct as 
 no locking/unlocking is done at fork time and I don't know if it's 
 needed or not. Not really a threading expert :)

 Saying that, this patch has been running on my server and workstation 
 for a few days now with no adverse effects.
 Comments welcome.
 Roy

 Index: gen/arc4random.c
 ===================================================================
 RCS file: /cvsroot/src/lib/libc/gen/arc4random.c,v
 retrieving revision 1.21
 diff -u -p -r1.21 arc4random.c
 --- gen/arc4random.c    17 Oct 2013 23:56:17 -0000      1.21
 +++ gen/arc4random.c    31 May 2014 00:03:55 -0000
 @@ -33,6 +33,7 @@ __RCSID("$NetBSD: arc4random.c,v 1.21 20
   #include "namespace.h"
   #include "reentrant.h"
   #include <fcntl.h>
 +#include <pthread.h>
   #include <stdlib.h>
   #include <unistd.h>
   #include <sys/types.h>
 @@ -49,11 +50,12 @@ __weak_alias(arc4random_uniform,_arc4ran
   #endif

   struct arc4_stream {
 -       uint8_t stirred;
 +       uint8_t inited;
          uint8_t pad;
          uint8_t i;
          uint8_t j;
          uint8_t s[(uint8_t)~0u + 1u];   /* 256 to you and me */
 +       size_t count;
          mutex_t mtx;
   };

 @@ -67,7 +69,7 @@ struct arc4_stream {
                          mutex_unlock(&(rs)->mtx);      \
          }
   #else
 -#define LOCK(rs)
 +#define LOCK(rs)
   #define UNLOCK(rs)
   #endif

 @@ -77,8 +79,8 @@ struct arc4_stream {
   #define S64(n) S16(n), S16(n + 16), S16(n + 32), S16(n + 48)
   #define S256 S64(0), S64(64), S64(128), S64(192)

 -static struct arc4_stream rs = { .i = 0xff, .j = 0, .s = { S256 },
 -               .stirred = 0, .mtx = MUTEX_INITIALIZER };
 +static struct arc4_stream rs = { .inited = 0, .i = 0xff, .j = 0, .s = { 
 S256 },
 +               .count = 0, .mtx = MUTEX_INITIALIZER };

   #undef S
   #undef S4
 @@ -91,14 +93,22 @@ static __noinline void arc4_stir(struct
   static inline uint8_t arc4_getbyte(struct arc4_stream *);
   static inline uint32_t arc4_getword(struct arc4_stream *);

 -static inline int
 +static void
 +arc4_forked(void)
 +{
 +
 +       /* Reset the counter to a force new stir after forking */
 +       rs.count = 0;
 +}
 +
 +static inline void
   arc4_check_init(struct arc4_stream *as)
   {
 -       if (__predict_true(rs.stirred))
 -               return 0;

 -       arc4_stir(as);
 -       return 1;
 +       if (!__predict_true(as->inited)) {
 +               as->inited = 1;
 +               pthread_atfork(NULL, NULL, arc4_forked);
 +       }
   }

   static inline void
 @@ -124,6 +134,8 @@ arc4_stir(struct arc4_stream *as)
          size_t len;
          size_t i, j;

 +       arc4_check_init(as);
 +         */
 -       for (j = 0; j < __arraycount(as->s) * 4; j++)
 +       for (j = 0; j < __arraycount(as->s) * sizeof(uint32_t); j++)
                  arc4_getbyte(as);

 -       as->stirred = 1;
 +       /* Stir again after swallowing 1600000 bytes or if the pid 
 changes */
 +       as->count = 1600000;
 +}
 +
 +static inline void
 +arc4_stir_if_needed(struct arc4_stream *as, size_t len)
 +{
 +
 +       if (as->count <= len)
 +               arc4_stir(as);
 +       else
 +               as->count -= len;
   }

   static __inline uint8_t
 @@ -169,6 +192,7 @@ arc4_getbyte_ij(struct arc4_stream *as,
   static inline uint8_t
   arc4_getbyte(struct arc4_stream *as)
   {
 +
          return arc4_getbyte_ij(as, &as->i, &as->j);
   }

 @@ -176,6 +200,7 @@ static inline uint32_t
   arc4_getword(struct arc4_stream *as)
   {
          uint32_t val;
 +
          val = arc4_getbyte(as) << 24;
          val |= arc4_getbyte(as) << 16;
          val |= arc4_getbyte(as) << 8;
 @@ -186,6 +211,7 @@ arc4_getword(struct arc4_stream *as)
   void
   arc4random_stir(void)        UNLOCK(&rs);
 @@ -194,8 +220,9 @@ arc4random_stir(void)
   void
   arc4random_addrandom(u_char *dat, int datlen)
   {
 +
          LOCK(&rs);
 -       arc4_check_init(&rs);
 +       arc4_stir_if_needed(&rs, datlen);
          arc4_addrandom(&rs, dat, datlen);
          UNLOCK(&rs);
   }
 @@ -206,7 +233,7 @@ arc4random(void)
          uint32_t v;

          LOCK(&rs);
 -       arc4_check_init(&rs);
 +       arc4_stir_if_needed(&rs, sizeof(v));
          v = arc4_getword(&rs);
          UNLOCK(&rs);
          return v;
 @@ -220,7 +247,7 @@ arc4random_buf(void *buf, size_t len)
          uint8_t i, j;

          LOCK(&rs);
 -       arc4_check_init(&rs);
 +       arc4_stir_if_needed(&rs, len);

          /* cache i and j - compiler can't know 'buf' doesn't alias them 
 */
          i = rs.i;
 @@ -263,7 +290,7 @@ arc4random_uniform(uint32_t upper_bound)
          min = (0xFFFFFFFFU - upper_bound + 1) % upper_bound;

          LOCK(&rs);
 -       arc4_check_init(&rs);
 +       arc4_stir_if_needed(&rs, sizeof(r));

          /*
           * This could theoretically loop forever but each retry has

   {
 +
          LOCK(&rs);
          arc4_stir(&rs);

          /*
           * This code once opened and read /dev/urandom on each
           * call.  That causes repeated rekeying of the kernel stream
 @@ -146,10 +158,21 @@ arc4_stir(struct arc4_stream *as)
           * paper "Weaknesses in the Key Scheduling Algorithm of RC4"
           * by Fluher, Mantin, and Shamir.  (N = 256 in our case.)

From: "Roy Marples" <roy@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc: 
Subject: PR/25367 CVS commit: src/lib/libc/gen
Date: Sat, 7 Jun 2014 20:55:47 +0000

 Module Name:	src
 Committed By:	roy
 Date:		Sat Jun  7 20:55:47 UTC 2014

 Modified Files:
 	src/lib/libc/gen: arc4random.c

 Log Message:
 Re-stir after forking, fixes PR lib/25367.
 Re-stir after consuming 1600000 bytes, fixes PR lib/45952.


 To generate a diff of this commit:
 cvs rdiff -u -r1.21 -r1.22 src/lib/libc/gen/arc4random.c

 Please note that diffs are not public domain; they are subject to the
 copyright notices on the relevant files.

State-Changed-From-To: open->closed
State-Changed-By: dholland@NetBSD.org
State-Changed-When: Mon, 21 Jul 2014 08:21:46 +0000
State-Changed-Why:
fixed, thanks


>Unformatted:

NetBSD Home
NetBSD PR Database Search

(Contact us) $NetBSD: query-full-pr,v 1.39 2013/11/01 18:47:49 spz Exp $
$NetBSD: gnats_config.sh,v 1.8 2006/05/07 09:23:38 tsutsui Exp $
Copyright © 1994-2007 The NetBSD Foundation, Inc. ALL RIGHTS RESERVED.