More randomness or less
CVE-2008-0166 is an infamous example of using uninitialized memory for random number generation. A Debian maintainer commented out two lines of code to silence Valgrind, which complained about the uses of uninitialized memory as a “bonus” source of entropy, and this change caused OpenSSL to generate bogus keys on Debian-based systems.
Actually, using uninitialized memory has always been a very bad idea, not only because it confuses developers and tools like Valgrind, but because a smart C compiler will kick you in the teeth. Here is one example in FreeBSD and Mac OS X libc.
The srandomdev() function is used to seed random(), according
to its manpage, “suitable for cryptographic use.” It first
tries /dev/random, which is non-blocking on FreeBSD and Mac OS X;
if that fails, it falls back to using current time and pid, with
some amazing extra bits.
struct timeval tv;
unsigned long junk;
gettimeofday(&tv, NULL);
srandom((getpid() << 16) ^ tv.tv_sec ^ tv.tv_usec ^ junk);
You can see that the seed is computed using results from gettimeofday()
and getpid(), mixed with the value of an uninitialized stack
variable junk. Here’s the corresponding assembly code (/usr/lib/libSystem.B.dylib)
from Mac OS X 10.6 (Snow Leopard).
leaq 0xe0(%rbp),%rdi
xorl %esi,%esi
callq 0x001422ca ; symbol stub for: _gettimeofday
callq 0x00142270 ; symbol stub for: _getpid
movq 0xe0(%rbp),%rdx
movl 0xe8(%rbp),%edi
xorl %edx,%edi
shll $0x10,%eax
xorl %eax,%edi
xorl %ebx,%edi
callq 0x00142d68 ; symbol stub for: _srandom
Everything looks good so far.
Now let’s look at the same code
(/usr/lib/system/libsystem_c.dylib)
from 10.7 (Lion) and 10.8 (Mountain Lion).
leaq 0xd8(%rbp),%rdi
xorl %esi,%esi
callq 0x000a427e ; symbol stub for: _gettimeofday
callq 0x000a3882 ; symbol stub for: _getpid
callq 0x000a4752 ; symbol stub for: _srandom
Wait, the entire seed computation is gone! The results of
gettimeofday() and getpid() are not used at all; srandom()
is called with some “garbage” value.
I guess Apple has switched from GCC to LLVM for compiling libc in
newer Mac OS X. Since the C code contains undefined behavior, the
use of uninitialized variable junk, LLVM optimizes away the
computation more aggressively. You should see the same assembly
if compiling FreeBSD using LLVM.
There are several interesting questions to explore.
-
Is it possible to trigger this by somehow failing
/dev/randomon FreeBSD and Mac OS X? -
How random is the
srandom()seed now compiled using LLVM, which is just the value of%edi? Looks like it’s the lower 32 bits of the address of the stack variabletv. -
Does GCC generate “correct” code? Probably not. The last
xorlinstruction uses%ebx, which is unlikely to correspond to the value ofjunkbut the file descriptor of/dev/random.
movl %ebx,%edi
callq 0x00141d48 ; symbol stub for: _close$NOCANCEL
In short, just don’t use uninitialized memory for more randomness.
The use of junk
was introduced
in 1997. Google shows a bunch of similar usages and fixes.
-
sranddev()shares the same fall-back code. -
DragonFly BSD developers added comments “XXX left uninitialized on purpose” for
junk. -
OpenBSD developers removed the code in 2005.
-
Varnish developers fixed a similar usage, though the code is still being used by others. BTW, you can find an interesting comparison
fd >= 0there, wherefdis a pointer!