|  | /* Copyright (c) 2018, Google Inc. | 
|  | * | 
|  | * Permission to use, copy, modify, and/or distribute this software for any | 
|  | * purpose with or without fee is hereby granted, provided that the above | 
|  | * copyright notice and this permission notice appear in all copies. | 
|  | * | 
|  | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | 
|  | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | 
|  | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY | 
|  | * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | 
|  | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION | 
|  | * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | 
|  | * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ | 
|  |  | 
|  | #include <openssl/rand.h> | 
|  |  | 
|  | #include <stdio.h> | 
|  |  | 
|  | #include <gtest/gtest.h> | 
|  |  | 
|  | #include <openssl/span.h> | 
|  |  | 
|  | #include "../fipsmodule/rand/fork_detect.h" | 
|  | #include "../fipsmodule/rand/internal.h" | 
|  | #include "../test/abi_test.h" | 
|  | #include "../test/test_util.h" | 
|  |  | 
|  | #if defined(OPENSSL_THREADS) | 
|  | #include <array> | 
|  | #include <thread> | 
|  | #include <vector> | 
|  | #endif | 
|  |  | 
|  | #if !defined(OPENSSL_WINDOWS) | 
|  | #include <errno.h> | 
|  | #include <sys/types.h> | 
|  | #include <sys/wait.h> | 
|  | #include <unistd.h> | 
|  | #endif | 
|  |  | 
|  |  | 
|  | // These tests are, strictly speaking, flaky, but we use large enough buffers | 
|  | // that the probability of failing when we should pass is negligible. | 
|  |  | 
|  | TEST(RandTest, NotObviouslyBroken) { | 
|  | static const uint8_t kZeros[256] = {0}; | 
|  |  | 
|  | uint8_t buf1[256], buf2[256]; | 
|  | RAND_bytes(buf1, sizeof(buf1)); | 
|  | RAND_bytes(buf2, sizeof(buf2)); | 
|  |  | 
|  | EXPECT_NE(Bytes(buf1), Bytes(buf2)); | 
|  | EXPECT_NE(Bytes(buf1), Bytes(kZeros)); | 
|  | EXPECT_NE(Bytes(buf2), Bytes(kZeros)); | 
|  | } | 
|  |  | 
|  | #if !defined(OPENSSL_WINDOWS) && !defined(OPENSSL_IOS) && \ | 
|  | !defined(OPENSSL_FUCHSIA) && !defined(BORINGSSL_UNSAFE_DETERMINISTIC_MODE) | 
|  | static bool ForkAndRand(bssl::Span<uint8_t> out, bool fork_unsafe_buffering) { | 
|  | int pipefds[2]; | 
|  | if (pipe(pipefds) < 0) { | 
|  | perror("pipe"); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // This is a multi-threaded process, but GTest does not run tests concurrently | 
|  | // and there currently are no threads, so this should be safe. | 
|  | pid_t child = fork(); | 
|  | if (child < 0) { | 
|  | perror("fork"); | 
|  | close(pipefds[0]); | 
|  | close(pipefds[1]); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if (child == 0) { | 
|  | // This is the child. Generate entropy and write it to the parent. | 
|  | close(pipefds[0]); | 
|  | if (fork_unsafe_buffering) { | 
|  | RAND_enable_fork_unsafe_buffering(-1); | 
|  | } | 
|  | RAND_bytes(out.data(), out.size()); | 
|  | while (!out.empty()) { | 
|  | ssize_t ret = write(pipefds[1], out.data(), out.size()); | 
|  | if (ret < 0) { | 
|  | if (errno == EINTR) { | 
|  | continue; | 
|  | } | 
|  | perror("write"); | 
|  | _exit(1); | 
|  | } | 
|  | out = out.subspan(static_cast<size_t>(ret)); | 
|  | } | 
|  | _exit(0); | 
|  | } | 
|  |  | 
|  | // This is the parent. Read the entropy from the child. | 
|  | close(pipefds[1]); | 
|  | while (!out.empty()) { | 
|  | ssize_t ret = read(pipefds[0], out.data(), out.size()); | 
|  | if (ret <= 0) { | 
|  | if (ret == 0) { | 
|  | fprintf(stderr, "Unexpected EOF from child.\n"); | 
|  | } else { | 
|  | if (errno == EINTR) { | 
|  | continue; | 
|  | } | 
|  | perror("read"); | 
|  | } | 
|  | close(pipefds[0]); | 
|  | return false; | 
|  | } | 
|  | out = out.subspan(static_cast<size_t>(ret)); | 
|  | } | 
|  | close(pipefds[0]); | 
|  |  | 
|  | // Wait for the child to exit. | 
|  | int status; | 
|  | if (waitpid(child, &status, 0) < 0) { | 
|  | perror("waitpid"); | 
|  | return false; | 
|  | } | 
|  | if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { | 
|  | fprintf(stderr, "Child did not exit cleanly.\n"); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | TEST(RandTest, Fork) { | 
|  | static const uint8_t kZeros[16] = {0}; | 
|  |  | 
|  | // Draw a little entropy to initialize any internal PRNG buffering. | 
|  | uint8_t byte; | 
|  | RAND_bytes(&byte, 1); | 
|  |  | 
|  | // Draw entropy in two child processes and the parent process. This test | 
|  | // intentionally uses smaller buffers than the others, to minimize the chance | 
|  | // of sneaking by with a large enough buffer that we've since reseeded from | 
|  | // the OS. | 
|  | // | 
|  | // All child processes should have different PRNGs, including the ones that | 
|  | // disavow fork-safety. Although they are produced by fork, they themselves do | 
|  | // not fork after that call. | 
|  | uint8_t bufs[5][16]; | 
|  | ASSERT_TRUE(ForkAndRand(bufs[0], /*fork_unsafe_buffering=*/false)); | 
|  | ASSERT_TRUE(ForkAndRand(bufs[1], /*fork_unsafe_buffering=*/false)); | 
|  | ASSERT_TRUE(ForkAndRand(bufs[2], /*fork_unsafe_buffering=*/true)); | 
|  | ASSERT_TRUE(ForkAndRand(bufs[3], /*fork_unsafe_buffering=*/true)); | 
|  | RAND_bytes(bufs[4], sizeof(bufs[4])); | 
|  |  | 
|  | // All should be different and non-zero. | 
|  | for (const auto &buf : bufs) { | 
|  | EXPECT_NE(Bytes(buf), Bytes(kZeros)); | 
|  | } | 
|  | for (size_t i = 0; i < OPENSSL_ARRAY_SIZE(bufs); i++) { | 
|  | for (size_t j = 0; j < i; j++) { | 
|  | EXPECT_NE(Bytes(bufs[i]), Bytes(bufs[j])) | 
|  | << "buffers " << i << " and " << j << " matched"; | 
|  | } | 
|  | } | 
|  | } | 
|  | #endif  // !OPENSSL_WINDOWS && !OPENSSL_IOS && | 
|  | // !OPENSSL_FUCHSIA && !BORINGSSL_UNSAFE_DETERMINISTIC_MODE | 
|  |  | 
|  | #if defined(OPENSSL_THREADS) | 
|  | static void RunConcurrentRands(size_t num_threads) { | 
|  | static const uint8_t kZeros[256] = {0}; | 
|  |  | 
|  | std::vector<std::array<uint8_t, 256>> bufs(num_threads); | 
|  | std::vector<std::thread> threads(num_threads); | 
|  |  | 
|  | for (size_t i = 0; i < num_threads; i++) { | 
|  | threads[i] = | 
|  | std::thread([i, &bufs] { RAND_bytes(bufs[i].data(), bufs[i].size()); }); | 
|  | } | 
|  | for (size_t i = 0; i < num_threads; i++) { | 
|  | threads[i].join(); | 
|  | } | 
|  |  | 
|  | for (size_t i = 0; i < num_threads; i++) { | 
|  | EXPECT_NE(Bytes(bufs[i]), Bytes(kZeros)); | 
|  | for (size_t j = i + 1; j < num_threads; j++) { | 
|  | EXPECT_NE(Bytes(bufs[i]), Bytes(bufs[j])); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // Test that threads may concurrently draw entropy without tripping TSan. | 
|  | TEST(RandTest, Threads) { | 
|  | constexpr size_t kFewerThreads = 10; | 
|  | constexpr size_t kMoreThreads = 20; | 
|  |  | 
|  | // Draw entropy in parallel. | 
|  | RunConcurrentRands(kFewerThreads); | 
|  | // Draw entropy in parallel with higher concurrency than the previous maximum. | 
|  | RunConcurrentRands(kMoreThreads); | 
|  | // Draw entropy in parallel with lower concurrency than the previous maximum. | 
|  | RunConcurrentRands(kFewerThreads); | 
|  | } | 
|  | #endif  // OPENSSL_THREADS | 
|  |  | 
|  | #if defined(OPENSSL_X86_64) && defined(SUPPORTS_ABI_TEST) | 
|  | TEST(RandTest, RdrandABI) { | 
|  | if (!have_rdrand()) { | 
|  | fprintf(stderr, "rdrand not supported. Skipping.\n"); | 
|  | return; | 
|  | } | 
|  |  | 
|  | uint8_t buf[32]; | 
|  | CHECK_ABI_SEH(CRYPTO_rdrand, buf); | 
|  | CHECK_ABI_SEH(CRYPTO_rdrand_multiple8_buf, nullptr, 0); | 
|  | CHECK_ABI_SEH(CRYPTO_rdrand_multiple8_buf, buf, 8); | 
|  | CHECK_ABI_SEH(CRYPTO_rdrand_multiple8_buf, buf, 16); | 
|  | CHECK_ABI_SEH(CRYPTO_rdrand_multiple8_buf, buf, 24); | 
|  | CHECK_ABI_SEH(CRYPTO_rdrand_multiple8_buf, buf, 32); | 
|  | } | 
|  | #endif  // OPENSSL_X86_64 && SUPPORTS_ABI_TEST |