Skip to content

Instantly share code, notes, and snippets.

@ppelleti
Created August 21, 2013 06:33
Show Gist options
  • Save ppelleti/6290984 to your computer and use it in GitHub Desktop.
Save ppelleti/6290984 to your computer and use it in GitHub Desktop.
This is a simple program which demonstrates that OpenSSL's default random number generator is not fork-safe if the pids wrap.
/* Patrick Pelletier, public domain / cc0
*
* OpenSSL's default random number generator (RAND_SSLeay) is not
* fork-safe in the case where the child pids wrap around without the
* parent having used or reseeded the RNG in the interim. Which makes
* this a rare problem, but one which recently cropped up on Android:
* https://plus.google.com/+AndroidDevelopers/posts/YxWzeNQMJS2
* and Nikolay Elenkov wrote a program to demonstrate the bug on Android:
* https://gist.github.com/nelenkov/581f9be65dcc0b6b35b9
*
* The program below demonstrates the same thing, but in plain C,
* using only OpenSSL, so it can be run on Linux or another POSIX-like
* operating system.
*
* compile me like this:
* gcc -Wall -O3 rand-trouble.c -o rand-trouble -lcrypto -pthread
* run me like this:
* ./rand-trouble > randies.txt
* and then view the results like this:
* sort randies.txt | uniq -c | sort | less
*/
#include <openssl/rand.h>
#include <openssl/engine.h>
#include <openssl/conf.h>
#include <openssl/err.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <pthread.h>
typedef void (*voidfunc) (void);
static void print_random (void)
{
unsigned char buf[16];
RAND_bytes (buf, sizeof (buf));
size_t i;
for (i = 0 ; i < sizeof (buf) ; i++)
printf ("%02x", buf[i]);
printf (" (pid %u)\n", getpid ());
/* this is important, or we get duplicated stdio across fork() */
fflush (stdout);
}
int main (int argc, char **argv)
{
/* Load the human readable error strings for libcrypto */
ERR_load_crypto_strings();
/* Load all digest and cipher algorithms */
OpenSSL_add_all_algorithms();
/* Load config file, and other important initialisation and/or
* initialization. In particular, this is what, very indirectly,
* sets up the RdRand engine, as I describe here:
* http://wiki.opensslfoundation.com/index.php/Talk:Libcrypto_API#Initialization.2C_OPENSSL_conf_and_engines.3F
* So if you have RdRand and want to see how OpenSSL behaves without it,
* comment out the following line: */
OPENSSL_config(NULL);
/* Print version of OpenSSL. */
fprintf (stderr, "%s\n", SSLeay_version (SSLEAY_VERSION));
/* Print whether we're using a rand ENGINE. If we're using RdRand,
* we don't expect any problems, but if we're using RAND_SSLeay (the
* default if there's no ENGINE), then we should get pid wraparound
* issues. */
ENGINE *rnd = ENGINE_get_default_RAND ();
if (rnd)
fprintf (stderr, "rand engine: %s\n", ENGINE_get_name (rnd));
else
fprintf (stderr, "no rand engine\n");
/* Uncomment this to demonstrate a solution. We would only need
* to reseed the generator in either the parent or the child, but
* for the sake of overkill, we do both. */
// pthread_atfork (NULL, (voidfunc) RAND_poll, (voidfunc) RAND_poll);
/* This is important, because we first use RAND_bytes (and thus
* provoke initialization) in the parent, which is what leads to
* the problem. If you comment this out, each child will seed
* itself from /dev/urandom, and there will be no duplicates. */
print_random ();
int i;
for (i = 0 ; i < 65536 ; i++)
{
pid_t pid = fork ();
if (pid < 0)
{
perror ("fork");
exit (EXIT_FAILURE);
}
else if (pid == 0)
{
print_random ();
exit (EXIT_SUCCESS);
}
else
{
int status;
if (waitpid (pid, &status, 0) < 0)
{
perror ("waitpid");
exit (EXIT_FAILURE);
}
}
}
return EXIT_SUCCESS;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment