[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
This commit is contained in:
Louis Dionne 2021-12-21 11:49:04 -05:00
parent 6db04b97e6
commit d202c76441
6 changed files with 52 additions and 10 deletions

View file

@ -114,6 +114,11 @@ ABI Changes
comment `here <https://reviews.llvm.org/D109459>`_ if you are broken by this change
and need to define the macro.
- On Apple platforms, ``std::random_device`` is now implemented on top of ``arc4random()``
instead of reading from ``/dev/urandom``. Any implementation-defined token used when
constructing a ``std::random_device`` will now be ignored instead of interpreted as a
file to read entropy from.
Build System Changes
--------------------

View file

@ -107,6 +107,13 @@
# define _LIBCPP_ABI_ENABLE_UNIQUE_PTR_TRIVIAL_ABI
// Enable clang::trivial_abi on std::shared_ptr and std::weak_ptr
# define _LIBCPP_ABI_ENABLE_SHARED_PTR_TRIVIAL_ABI
// std::random_device holds some state when it uses an implementation that gets
// entropy from a file (see _LIBCPP_USING_DEV_RANDOM). When switching from this
// implementation to another one on a platform that has already shipped
// std::random_device, one needs to retain the same object layout to remain ABI
// compatible. This switch removes these workarounds for platforms that don't care
// about ABI compatibility.
# define _LIBCPP_ABI_NO_RANDOM_DEVICE_COMPATIBILITY_LAYOUT
#elif _LIBCPP_ABI_VERSION == 1
# if !defined(_LIBCPP_OBJECT_FORMAT_COFF)
// Enable compiling copies of now inline methods into the dylib to support
@ -371,7 +378,7 @@
// Use rand_s(), for use on Windows.
// When this option is used, the token passed to `std::random_device`'s
// constructor *must* be "/dev/urandom" -- anything else is an error.
#if defined(__OpenBSD__)
#if defined(__OpenBSD__) || defined(__APPLE__)
# define _LIBCPP_USING_ARC4_RANDOM
#elif defined(__wasi__)
# define _LIBCPP_USING_GETENTROPY

View file

@ -27,7 +27,26 @@ class _LIBCPP_TYPE_VIS random_device
{
#ifdef _LIBCPP_USING_DEV_RANDOM
int __f_;
#elif !defined(_LIBCPP_ABI_NO_RANDOM_DEVICE_COMPATIBILITY_LAYOUT)
# if defined(__clang__)
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wunused-private-field"
# endif
// Apple platforms used to use the `_LIBCPP_USING_DEV_RANDOM` code path, and now
// use `arc4random()` as of this comment. In order to avoid breaking the ABI, we
// retain the same layout as before.
# if defined(__APPLE__)
int __padding_; // padding to fake the `__f_` field above
# endif
// ... vendors can add workarounds here if they switch to a different representation ...
# if defined(__clang__)
# pragma clang diagnostic pop
# endif
#endif
public:
// types
typedef unsigned result_type;

View file

@ -68,10 +68,8 @@ random_device::operator()()
#elif defined(_LIBCPP_USING_ARC4_RANDOM)
random_device::random_device(const string& __token)
random_device::random_device(const string&)
{
if (__token != "/dev/urandom")
__throw_system_error(ENOENT, ("random device not supported " + __token).c_str());
}
random_device::~random_device()

View file

@ -9,6 +9,11 @@
// 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>
@ -59,13 +64,24 @@ int main(int, char**) {
}
// Check the validity of various tokens
{
check_random_device_invalid("wrong file");
check_random_device_invalid("/dev/whatever");
#if defined(_LIBCPP_USING_ARC4_RANDOM)
check_random_device_valid("/dev/urandom");
#if defined(_LIBCPP_USING_DEV_RANDOM)
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
}

View file

@ -6,9 +6,6 @@
//
//===----------------------------------------------------------------------===//
// See https://llvm.org/PR20183
// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{9|10|11}}
// UNSUPPORTED: libcpp-has-no-random-device
// <random>