Louis Dionne d202c76441 [libc++] Start using arc4random() to implement std::random_device on Apple
On Apple platforms, arc4random is faster than /dev/urandom, and it is
the recommended user-space RNG according to Apple's own OS folks.

This commit adds an ABI switch to guard ABI-break-protections in
std::random_device, and starts using arc4random instead of /dev/urandom
to implement std::random_device on Apple platforms.

Note that previously, `std::random_device` would allow passing a custom
token to its constructor, and that token would be interpreted as the name
of a file to read entropy from. This was implementation-defined and
undocumented. After this change, Apple platforms will be using arc4random()
instead, and any custom token passed to the constructor will be ignored.
This behavioral change will also impact other platforms that use the
arc4random() implementation, such as OpenBSD. This should be fine since
that is effectively a relaxation of the constructor's requirements.

rdar://86638350

Differential Revision: https://reviews.llvm.org/D116045
2022-01-12 11:24:23 -05:00

110 lines
3.2 KiB
C++

//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// See https://llvm.org/PR20183
// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{9|10|11}}
// The behavior of std::random_device changed on Apple platforms with
// https://llvm.org/D116045.
// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{9|10|11|12|13|14|15}}
// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx{{11|12}}
// UNSUPPORTED: libcpp-has-no-random-device
// <random>
// class random_device;
// explicit random_device(const string& token = implementation-defined); // before C++20
// random_device() : random_device(implementation-defined) {} // C++20
// explicit random_device(const string& token); // C++20
// For the following ctors, the standard states: "The semantics and default
// value of the token parameter are implementation-defined". Implementations
// therefore aren't required to accept any string, but the default shouldn't
// throw.
#include <random>
#include <system_error>
#include <cassert>
#if !defined(_WIN32)
#include <unistd.h>
#endif
#include "test_macros.h"
#if TEST_STD_VER >= 11
#include "test_convertible.h"
#endif
void check_random_device_valid(const std::string &token) {
std::random_device r(token);
}
void check_random_device_invalid(const std::string &token) {
#ifndef TEST_HAS_NO_EXCEPTIONS
try {
std::random_device r(token);
LIBCPP_ASSERT(false);
} catch (const std::system_error&) {
}
#else
((void)token);
#endif
}
int main(int, char**) {
{
std::random_device r;
}
// Check the validity of various tokens
{
#if defined(_LIBCPP_USING_ARC4_RANDOM)
check_random_device_valid("/dev/urandom");
check_random_device_valid("/dev/random");
check_random_device_valid("/dev/null");
check_random_device_valid("/dev/nonexistent");
check_random_device_valid("wrong file");
#elif defined(_LIBCPP_USING_DEV_RANDOM)
check_random_device_valid("/dev/urandom");
check_random_device_valid("/dev/random");
check_random_device_valid("/dev/null");
check_random_device_invalid("/dev/nonexistent");
check_random_device_invalid("wrong file");
#else
check_random_device_valid("/dev/urandom");
check_random_device_invalid("/dev/random");
check_random_device_invalid("/dev/null");
check_random_device_invalid("/dev/nonexistent");
check_random_device_invalid("wrong file");
#endif
}
#if !defined(_WIN32)
// Test that random_device(const string&) properly handles getting
// a file descriptor with the value '0'. Do this by closing the standard
// streams so that the descriptor '0' is available.
{
int ec;
ec = close(STDIN_FILENO);
assert(!ec);
ec = close(STDOUT_FILENO);
assert(!ec);
ec = close(STDERR_FILENO);
assert(!ec);
std::random_device r;
}
#endif // !defined(_WIN32)
#if TEST_STD_VER >= 11
static_assert(test_convertible<std::random_device>(), "");
#endif
return 0;
}