Due to an involuntary integer overflow, the randomread() and
randomwrite() functions can be tricked into entering an endless loop.
That loop can be triggered by performing a read() of UINT_MAX + 1 bytes
from /dev/random on a platform where sizeof(size_t) > sizeof(u_int).
This holds true for e.g. amd64. A minimal program like the one included
below can be used to trigger the bug.
---
#include <err.h>
#include <fcntl.h>
#include <unistd.h>
int
main(void)
{
int fd;
ssize_t n;
fd = open("/dev/random", O_RDONLY);
if (fd == -1)
err(1, "open");
n = read(fd, NULL, (size_t)1 << 32);
if (n == -1)
err(1, "read");
(void)close(fd);
return (0);
}
---
Replacing min() with ulmin() prevents the busy loop.
Index: dev/rnd.c
===================================================================
RCS file: /cvs/src/sys/dev/rnd.c,v
retrieving revision 1.173
diff -u -p -r1.173 rnd.c
--- dev/rnd.c 14 Mar 2015 03:38:46 -0000 1.173
+++ dev/rnd.c 8 Apr 2015 12:30:32 -0000
@@ -840,16 +840,16 @@ randomread(dev_t dev, struct uio *uio, i
}
while (ret == 0 && uio->uio_resid > 0) {
- int n = min(POOLBYTES, uio->uio_resid);
+ size_t n = ulmin(POOLBYTES, uio->uio_resid);
if (myctx) {
#ifndef KEYSTREAM_ONLY
memset(buf, 0, n);
#endif
- chacha_encrypt_bytes(&lctx, buf, buf, n);
+ chacha_encrypt_bytes(&lctx, buf, buf, (u32)n);
} else
arc4random_buf(buf, n);
- ret = uiomovei(buf, n, uio);
+ ret = uiomove(buf, n, uio);
if (ret == 0 && uio->uio_resid > 0)
yield();
}
@@ -872,14 +872,14 @@ randomwrite(dev_t dev, struct uio *uio,
buf = malloc(POOLBYTES, M_TEMP, M_WAITOK);
while (ret == 0 && uio->uio_resid > 0) {
- int n = min(POOLBYTES, uio->uio_resid);
+ size_t n = ulmin(POOLBYTES, uio->uio_resid);
- ret = uiomovei(buf, n, uio);
+ ret = uiomove(buf, n, uio);
if (ret != 0)
break;
while (n % sizeof(u_int32_t))
((u_int8_t *)buf)[n++] = 0;
- add_entropy_words(buf, n / 4);
+ add_entropy_words(buf, (u_int)(n / 4));
if (uio->uio_resid > 0)
yield();
newdata = 1;
Although size_t is unsigned long on all supported platforms, POSIX
only guarantees u_long to be at least 2**32 bits of size. At Bitrig
we have introduced szmin() and szmax() to libkern, which operates like
ulmin() and ulmax(), but with size_t values as input/output. If there
is interest, I'll be happy to produce a patch for this mailing list,
that adds szmin/max to libkern.
cheers,
natano