dsprenkels / randombytes

A portable C library for generating cypto-secure random bytes
MIT License
96 stars 37 forks source link

Binaries compiled using /dev/urandom on linux go into endless loop on joyent lx zones. #17

Closed Seep1959 closed 5 years ago

Seep1959 commented 5 years ago

I compiled a binary on linux, it used the /dev/urandom device.

lib/randombytes/randombytes.c: In function 'randombytes':
lib/randombytes/randombytes.c:235:11: note: #pragma message: Using /dev/urandom device
 #  pragma message("Using /dev/urandom device")

Relevant strace lines from joyent lx zone.

open("/dev/urandom", O_RDONLY)          = 3
ioctl(3, RNDGETENTCNT, 0x7fffffefdf10)  = -1 ENOTTY (Inappropriate ioctl for device)
open("/dev/random", O_RDONLY)           = 4
poll([{fd=4, events=POLLIN}], 1, -1)    = 1 ([{fd=4, revents=POLLIN}])
ioctl(3, RNDGETENTCNT, 0x7fffffefdf0c)  = -1 ENOTTY (Inappropriate ioctl for device)
poll([{fd=4, events=POLLIN}], 1, -1)    = 1 ([{fd=4, revents=POLLIN}])
ioctl(3, RNDGETENTCNT, 0x7fffffefdf0c)  = -1 ENOTTY (Inappropriate ioctl for device)
poll([{fd=4, events=POLLIN}], 1, -1)    = 1 ([{fd=4, revents=POLLIN}])
ioctl(3, RNDGETENTCNT, 0x7fffffefdf0c)  = -1 ENOTTY (Inappropriate ioctl for device)
poll([{fd=4, events=POLLIN}], 1, -1)    = 1 ([{fd=4, revents=POLLIN}])
ioctl(3, RNDGETENTCNT, 0x7fffffefdf0c)  = -1 ENOTTY (Inappropriate ioctl for device)

It will continue polling forever.

The issue appears to be here, https://github.com/dsprenkels/randombytes/blob/4b27a1454298cba3031aa411329735fae63cb771/randombytes.c#L142

I think this could be avoided by checking the return value after the call to randombytes_linux_get_entropy_avail() .

dsprenkels commented 5 years ago

Thank you!

I am wondering: Which linux version are you using?

If /dev/{u}random are both present, we should probably be able to get the current entropy count in another way. I think we could try to read /proc/sys/kernel/random/entropy_avail in this case as a fallback. Can you verify this (virtual) file exists on your platform?

In the worst case scenario we do have /dev/{u}random, but we do not have access to the entropy counter. I guess that, in this case, we can fall back to polling once on /dev/random and assuming that the kernel has enough entropy when it returns. I should probably do some measurements after early boot; how many polls on /dev/random are needed.

Seep1959 commented 5 years ago

Its a SmartOS lx zone running Centos7, the binary was compiled on a Centos 5 box using holy-build-box. The file /proc/sys/kernel/random/entropy_avail does exist on my platform and returns a high number of entropy.

[root@gitea01 ~]# uname -a
Linux gitea01.ipa.mydomain.tld 3.10.0 BrandZ virtual linux x86_64 x86_64 x86_64 GNU/Linux
[root@gitea01 ~]# cat /proc/sys/kernel/random/entropy_avail
8192
[root@gitea01 ~]# 

I'm not sure if I'm misunderstanding, but shouldn't /dev/urandom block until a suitable amount of entropy is gathered?

dsprenkels commented 5 years ago

I'm not sure if I'm misunderstanding, but shouldn't /dev/urandom block until a suitable amount of entropy is gathered?

On most systems, it should. Unfortunately, Linux is not one of them. As far as I know (at least older versions of) Linux just take the available entropy and go with it. Most of the time, this is not really dangerous, unless when we are at early boot.

dsprenkels commented 5 years ago

On BSD, it does block though. Maybe newer versions of Linux also, but I haven't checked. getrandom on Linux does* block (this has never been different).

dsprenkels commented 5 years ago

My proposed solution: If the ioctl is invalid, let's try to read from /proc/sys/kernel/random/entropy_avail and use that entropy counter as the fallback. If even /proc/sys/kernel/random/entropy_avail is unavailable, we can decide to only read from /dev/random (which is terribly slow, but will not happen anyway)?

dsprenkels commented 5 years ago

@Seep1959, with https://github.com/dsprenkels/randombytes/commit/cc43e4aaf4fc61a2ec4d8a1304bdf03ad9b2aff5, the issue should be completely fixed.