Skip to content

Commit bf00085

Browse files
author
tls
committed
Make arc4random far less greedy for entropy. Make arc4random actually
implement arc4 when used by threaded programs.
1 parent 8c9dc6d commit bf00085

File tree

1 file changed

+109
-65
lines changed

1 file changed

+109
-65
lines changed

lib/libc/gen/arc4random.c

Lines changed: 109 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* $NetBSD: arc4random.c,v 1.10 2011/02/04 22:07:07 christos Exp $ */
1+
/* $NetBSD: arc4random.c,v 1.11 2012/02/27 04:25:12 tls Exp $ */
22
/* $OpenBSD: arc4random.c,v 1.6 2001/06/05 05:05:38 pvalchev Exp $ */
33

44
/*
@@ -27,10 +27,11 @@
2727

2828
#include <sys/cdefs.h>
2929
#if defined(LIBC_SCCS) && !defined(lint)
30-
__RCSID("$NetBSD: arc4random.c,v 1.10 2011/02/04 22:07:07 christos Exp $");
30+
__RCSID("$NetBSD: arc4random.c,v 1.11 2012/02/27 04:25:12 tls Exp $");
3131
#endif /* LIBC_SCCS and not lint */
3232

3333
#include "namespace.h"
34+
#include "reentrant.h"
3435
#include <fcntl.h>
3536
#include <stdlib.h>
3637
#include <unistd.h>
@@ -44,13 +45,15 @@ __weak_alias(arc4random,_arc4random)
4445
#endif
4546

4647
struct arc4_stream {
48+
mutex_t mtx;
4749
uint8_t i;
4850
uint8_t j;
4951
uint8_t s[256];
5052
};
5153

5254
static int rs_initialized;
53-
static struct arc4_stream rs;
55+
/* XXX lint explodes with an internal error if only mtx is initialized! */
56+
static struct arc4_stream rs = { .i = 0, .mtx = MUTEX_INITIALIZER };
5457

5558
static inline void arc4_init(struct arc4_stream *);
5659
static inline void arc4_addrandom(struct arc4_stream *, u_char *, int);
@@ -89,40 +92,29 @@ arc4_addrandom(struct arc4_stream *as, u_char *dat, int datlen)
8992
static void
9093
arc4_stir(struct arc4_stream *as)
9194
{
92-
int fd;
93-
struct {
94-
struct timeval tv;
95-
u_int rnd[(128 - sizeof(struct timeval)) / sizeof(u_int)];
96-
} rdat;
95+
int rdat[128 / sizeof(int)];
9796
int n;
97+
int mib[2];
98+
unsigned int i;
99+
size_t len;
98100

99-
gettimeofday(&rdat.tv, NULL);
100-
fd = open("/dev/urandom", O_RDONLY);
101-
if (fd != -1) {
102-
read(fd, rdat.rnd, sizeof(rdat.rnd));
103-
close(fd);
104-
}
105-
#ifdef KERN_URND
106-
else {
107-
int mib[2];
108-
u_int i;
109-
size_t len;
110-
111-
/* Device could not be opened, we might be chrooted, take
112-
* randomness from sysctl. */
113-
114-
mib[0] = CTL_KERN;
115-
mib[1] = KERN_URND;
116-
117-
for (i = 0; i < sizeof(rdat.rnd) / sizeof(u_int); i++) {
118-
len = sizeof(u_int);
119-
if (sysctl(mib, 2, &rdat.rnd[i], &len, NULL, 0) == -1)
120-
break;
121-
}
101+
/*
102+
* This code once opened and read /dev/urandom on each
103+
* call. That causes repeated rekeying of the kernel stream
104+
* generator, which is very wasteful. Because of application
105+
* behavior, caching the fd doesn't really help. So we just
106+
* fill up the tank from sysctl, which is a tiny bit slower
107+
* for us but much friendlier to other entropy consumers.
108+
*/
109+
110+
mib[0] = CTL_KERN;
111+
mib[1] = KERN_URND;
112+
113+
for (i = 0; i < sizeof(rdat) / sizeof(int); i++) {
114+
len = sizeof(rdat[i]);
115+
if (sysctl(mib, 2, &rdat[i], &len, NULL, 0) == -1)
116+
abort();
122117
}
123-
#endif
124-
/* fd < 0 or failed sysctl ? Ah, what the heck. We'll just take
125-
* whatever was on the stack... */
126118

127119
arc4_addrandom(as, (void *) &rdat, sizeof(rdat));
128120

@@ -160,8 +152,8 @@ arc4_getword(struct arc4_stream *as)
160152
return val;
161153
}
162154

163-
void
164-
arc4random_stir(void)
155+
static inline void
156+
_arc4random_stir_unlocked(void)
165157
{
166158
if (!rs_initialized) {
167159
arc4_init(&rs);
@@ -171,23 +163,67 @@ arc4random_stir(void)
171163
}
172164

173165
void
174-
arc4random_addrandom(u_char *dat, int datlen)
166+
arc4random_stir(void)
167+
{
168+
#ifdef _REENTRANT
169+
if (__isthreaded) {
170+
mutex_lock(&rs.mtx);
171+
_arc4random_stir_unlocked();
172+
mutex_unlock(&rs.mtx);
173+
return;
174+
}
175+
#endif
176+
_arc4random_stir_unlocked();
177+
}
178+
179+
static inline void
180+
_arc4random_addrandom_unlocked(u_char *dat, int datlen)
175181
{
176182
if (!rs_initialized)
177-
arc4random_stir();
183+
arc4_stir(&rs);
178184
arc4_addrandom(&rs, dat, datlen);
179185
}
180186

181-
uint32_t
182-
arc4random(void)
187+
void
188+
arc4random_addrandom(u_char *dat, int datlen)
189+
{
190+
#ifdef _REENTRANT
191+
if (__isthreaded) {
192+
mutex_lock(&rs.mtx);
193+
_arc4random_addrandom_unlocked(dat, datlen);
194+
mutex_unlock(&rs.mtx);
195+
return;
196+
}
197+
#endif
198+
_arc4random_addrandom_unlocked(dat, datlen);
199+
}
200+
201+
static inline uint32_t
202+
_arc4random_unlocked(void)
183203
{
184204
if (!rs_initialized)
185-
arc4random_stir();
205+
arc4_stir(&rs);
186206
return arc4_getword(&rs);
187207
}
188208

189-
void
190-
arc4random_buf(void *buf, size_t len)
209+
uint32_t
210+
arc4random(void)
211+
{
212+
uint32_t v;
213+
#ifdef _REENTRANT
214+
if (__isthreaded) {
215+
mutex_lock(&rs.mtx);
216+
v = _arc4random_unlocked();
217+
mutex_unlock(&rs.mtx);
218+
return v;
219+
}
220+
#endif
221+
v = _arc4random_unlocked();
222+
return v;
223+
}
224+
225+
static void
226+
_arc4random_buf_unlocked(void *buf, size_t len)
191227
{
192228
uint8_t *bp = buf;
193229
uint8_t *ep = bp + len;
@@ -200,6 +236,20 @@ arc4random_buf(void *buf, size_t len)
200236
*bp++ = arc4_getbyte(&rs);
201237
}
202238

239+
void
240+
arc4random_buf(void *buf, size_t len)
241+
{
242+
#ifdef _REENTRANT
243+
if (__isthreaded) {
244+
mutex_lock(&rs.mtx);
245+
_arc4random_buf_unlocked(buf, len);
246+
mutex_unlock(&rs.mtx);
247+
return;
248+
} else
249+
#endif
250+
_arc4random_buf_unlocked(buf, len);
251+
}
252+
203253
/*-
204254
* Written by Damien Miller.
205255
* With simplifications by Jinmei Tatuya.
@@ -216,8 +266,8 @@ arc4random_buf(void *buf, size_t len)
216266
* [2^32 % upper_bound, 2^32[ which maps back to
217267
* [0, upper_bound[ after reduction modulo upper_bound.
218268
*/
219-
uint32_t
220-
arc4random_uniform(uint32_t upper_bound)
269+
static uint32_t
270+
_arc4random_uniform_unlocked(uint32_t upper_bound)
221271
{
222272
uint32_t r, min;
223273

@@ -243,7 +293,7 @@ arc4random_uniform(uint32_t upper_bound)
243293
* to re-roll (at all).
244294
*/
245295
if (!rs_initialized)
246-
arc4random_stir();
296+
arc4_stir(&rs);
247297
if (arc4_getbyte(&rs) & 1)
248298
(void)arc4_getbyte(&rs);
249299
do
@@ -253,24 +303,18 @@ arc4random_uniform(uint32_t upper_bound)
253303
return r % upper_bound;
254304
}
255305

256-
257-
#if 0
258-
/*-------- Test code for i386 --------*/
259-
#include <stdio.h>
260-
#include <machine/pctr.h>
261-
int
262-
main(int argc, char **argv)
306+
uint32_t
307+
arc4random_uniform(uint32_t upper_bound)
263308
{
264-
const int iter = 1000000;
265-
int i;
266-
pctrval v;
267-
268-
v = rdtsc();
269-
for (i = 0; i < iter; i++)
270-
arc4random();
271-
v = rdtsc() - v;
272-
v /= iter;
273-
274-
printf("%qd cycles\n", v);
275-
}
309+
uint32_t v;
310+
#ifdef _REENTRANT
311+
if (__isthreaded) {
312+
mutex_lock(&rs.mtx);
313+
v = _arc4random_uniform_unlocked(upper_bound);
314+
mutex_unlock(&rs.mtx);
315+
return v;
316+
}
276317
#endif
318+
v = _arc4random_uniform_unlocked(upper_bound);
319+
return v;
320+
}

0 commit comments

Comments
 (0)