Skip to content

Commit

Permalink
lib/randutils: improve getrandom() usage
Browse files Browse the repository at this point in the history
The getrandom() does not have to return all requested bytes (missing
entropy or when interrupted by signal). The current implementation in
util-linux stupidly asks for all random data again, rather than only
for missing bytes.

The current code also does not care if we repeat our requests for
ever; that's bad.

This patch uses the same way as we already use for reading from
/dev/urandom. It means:

 * repeat getrandom() for only missing bytes
 * limit number of unsuccessful request (16 times)
 * fallback to /dev/urandom on ENOSYS (old kernel or so...)

Addresses: #496
Signed-off-by: Karel Zak <kzak@redhat.com>
  • Loading branch information
karelzak committed Sep 20, 2017
1 parent 0e10ad0 commit fa94979
Showing 1 changed file with 46 additions and 18 deletions.
64 changes: 46 additions & 18 deletions lib/randutils.c
Original file line number Diff line number Diff line change
Expand Up @@ -95,27 +95,39 @@ int random_get_fd(void)
*/
void random_get_bytes(void *buf, size_t nbytes)
{
size_t i;
unsigned char *cp = (unsigned char *)buf;
size_t i, n = nbytes;
int lose_counter = 0;

#ifdef HAVE_GETRANDOM
errno = 0;
while (getrandom(buf, nbytes, 0) != (ssize_t)nbytes) {
if (errno == EINTR)
while (n > 0) {
int x;

errno = 0;
x = getrandom(cp, n, 0);
if (x > 0) { /* success */
n -= x;
cp += x;
lose_counter = 0;
} else if (errno == ENOSYS) { /* kernel without getrandom() */
break;
} else {
if (lose_counter++ > 16) /* entropy problem? */
break;
continue;
break;

This comment has been minimized.

Copy link
@pprindeville

pprindeville Oct 1, 2017

Contributor

Isn't the continue superfluous?

This comment has been minimized.

Copy link
@karelzak

karelzak Oct 2, 2017

Author Collaborator

Yeah, fixed. Thanks.

}
}

if (errno == ENOSYS)
#endif

/*
* We've been built against headers that support getrandom,
* but the running kernel does not.
* Fallback to reading from /dev/{u,}random as before
* We've been built against headers that support getrandom, but the
* running kernel does not. Fallback to reading from /dev/{u,}random
* as before
*/
#endif
{
size_t n = nbytes;
int fd = random_get_fd();
int lose_counter = 0;

if (fd >= 0) {
while (n > 0) {
Expand Down Expand Up @@ -153,7 +165,6 @@ void random_get_bytes(void *buf, size_t nbytes)
sizeof(ul_jrand_seed)-sizeof(unsigned short));
}
#endif

return;
}

Expand Down Expand Up @@ -181,15 +192,32 @@ const char *random_tell_source(void)
}

#ifdef TEST_PROGRAM_RANDUTILS
int main(int argc __attribute__ ((__unused__)),
char *argv[] __attribute__ ((__unused__)))
int main(int argc, char *argv[])
{
unsigned int v, i;
size_t i, n;
int64_t *vp, v;
char *buf;
size_t bufsz;

n = argc == 1 ? 16 : atoi(argv[1]);

/* generate and print 10 random numbers */
for (i = 0; i < 10; i++) {
printf("Multiple random calls:\n");
for (i = 0; i < n; i++) {
random_get_bytes(&v, sizeof(v));
printf("%d\n", v);
printf("#%02zu: %25ju\n", i, v);
}


printf("One random call:\n");
bufsz = n * sizeof(*vp);
buf = malloc(bufsz);
if (!buf)
err(EXIT_FAILURE, "failed to allocate buffer");

random_get_bytes(buf, bufsz);
for (i = 0; i < n; i++) {
vp = (int64_t *) (buf + (i * sizeof(*vp)));
printf("#%02zu: %25ju\n", i, *vp);
}

return EXIT_SUCCESS;
Expand Down

0 comments on commit fa94979

Please sign in to comment.