openssl: OpenSSL 3.0.0 RNG fork-safety regression? [security?]

Earlier versions of OpenSSL would take some pains to ensure that calling fork() was was safe in the presence of an RNG. This appears to be gone with the OpenSSL 3.0.0 code.

Specifically: when I write a program that calls RAND_bytes() in the main process, then forks several times, and each child process calls RAND_bytes(), I get the same result from RAND_bytes() in every one of the child processes. This is still the case when the child processes call RAND_poll(), which suggests to me that something is deeply wrong.

OpenSSL 1.1.1 behaves as expected in this case.

Found by Tor’s unit tests. (I don’t want to get preachy, but I’d strongly recommend that OpenSSL adds a regression test for this case: this is exactly the kind of a thing we worry about an RNG doing.)

Here is a demo program:

#include <openssl/rand.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>

int should_poll = 1;

void
check_rand_status(void)
{
	if (RAND_status() != 1) {
		puts("RAND_status is unhappy!");
	} else {
		// puts("RAND_status says everything is fine...e");
	}
}

void
try_rand(void)
{
	unsigned u;
	check_rand_status();
	if (should_poll)
		RAND_poll();
	check_rand_status();
	int r = RAND_bytes((void*)&u, sizeof(u));
	if (r == 0) {
		printf("Process %d: error!\n", (int)getpid());
	} else {
		printf("Process %d: rng value is %u.\n",
		       (int)getpid(), u);
	}
	check_rand_status();
}

int main(int argc, char **argv)
{
	check_rand_status();
	try_rand();
	check_rand_status();
	for (int i = 0; i < 8; ++i) {
		pid_t child;
		if ((child = fork())) {
			// parent.
		} else {
			try_rand();
			exit(0);
		}
	}
	return 0;
}

Here is an example output from openssl-master:

Process 680496: rng value is 1796322885.
Process 680497: rng value is 345659543.
Process 680498: rng value is 345659543.
Process 680499: rng value is 345659543.
Process 680500: rng value is 345659543.
Process 680501: rng value is 345659543.
Process 680502: rng value is 345659543.
Process 680503: rng value is 345659543.
Process 680504: rng value is 345659543.

Here is an example output from openssl 1.1.1g:

Process 680544: rng value is 2039804308.
Process 680545: rng value is 2038236718.
Process 680546: rng value is 1374486369.
Process 680547: rng value is 2441531771.
Process 680548: rng value is 892829712.
Process 680549: rng value is 1349569669.
Process 680550: rng value is 3048351340.
Process 680551: rng value is 2647008208.
Process 680552: rng value is 1731649072.

All tests run on Fedora 32.

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Comments: 34 (34 by maintainers)

Commits related to this issue

Most upvoted comments

Thank you again @nmathewson for reporting this important security issue. It would have deserved a CVE if it had slipped into the final release.

I moved my commit backwards on master and put it right in front of the 1.1.1 label. Then i did a bisect …

21038fa52b386fa0b3884567c68dd62e47eae903 is the first bad commit
commit 21038fa52b386fa0b3884567c68dd62e47eae903
Author: Matt Caswell <matt@openssl.org>
Date:   Mon Apr 8 17:19:59 2019 +0100

    Implement AES CTR ciphers in the default provider
    
    Reviewed-by: Paul Dale <paul.dale@oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/8700)

 crypto/evp/evp_enc.c                              |  3 +++
 providers/common/ciphers/aes.c                    | 11 +++++++++++
 providers/common/include/internal/provider_algs.h |  3 +++
 providers/default/defltprov.c                     |  3 +++
 4 files changed, 20 insertions(+)

The commit id is obv. wrong due to the rebase on top of my commit. The real commit id is 819a7ae9fc7721f675757c0925821f91b20dfc8f .

Since the commit does nothing special besides moving AES-CTR to the default provider, it occurs to me that either the default provider or providers in general are not fork safe.

Thanks for reporting. Im looking into it.