Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add _stdlib_random for more platforms #1

Merged
merged 8 commits into from
Feb 10, 2018
140 changes: 100 additions & 40 deletions stdlib/public/stubs/LibcShims.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,35 +11,67 @@
//===----------------------------------------------------------------------===//

#if defined(__APPLE__)
#define _REENTRANT
#include <math.h>
# define _REENTRANT
# include <math.h>
# include <Security/Security.h>
#endif
#include <random>
#include <type_traits>
#include <cmath>
#if defined(_WIN32)
#include <io.h>
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>

#if defined(__CYGWIN__)
# include <cygwin/version.h>
# if (CYGWIN_VERSION_API_MAJOR > 0) || (CYGWIN_VERSION_API_MINOR >= 306)
# include <sys/random.h>
# define SWIFT_STDLIB_USING_GETRANDOM
# endif
#endif

#if defined(__Fuchsia__)
# include <sys/random.h>
# define SWIFT_STDLIB_USING_GETENTROPY
#endif

#if defined(__linux__)
# include <linux/version.h>
# if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,17,0))
# include <features.h>
# if defined(__BIONIC__) || (defined(__GLIBC_PREREQ) && __GLIBC_PREREQ(2,25))
# include <sys/random.h>
# define SWIFT_STDLIB_USING_GETRANDOM
# endif
# endif
#endif

#if defined(_WIN32) && !defined(__CYGWIN__)
# include <io.h>
# define WIN32_LEAN_AND_MEAN
# define WIN32_NO_STATUS
# include <Windows.h>
# undef WIN32_NO_STATUS
# include <Ntstatus.h>
# include <Ntdef.h>
# include <Bcrypt.h>
#else
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
#include <sys/ioctl.h>
# include <pthread.h>
# include <semaphore.h>
# include <sys/ioctl.h>
# include <unistd.h>
#endif

#include <stdlib.h>
#include <cmath>
#include <errno.h>
#include <fcntl.h>
#include <random>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <type_traits>

#include "llvm/Support/DataTypes.h"
#include "swift/Basic/Lazy.h"
#include "swift/Runtime/Config.h"
#include "swift/Runtime/Debug.h"
#include "../SwiftShims/LibcShims.h"
#include "llvm/Support/DataTypes.h"

using namespace swift;

Expand Down Expand Up @@ -332,44 +364,72 @@ swift::_stdlib_cxx11_mt19937_uniform(__swift_uint32_t upper_bound) {
}

#if defined(__APPLE__)
#include <Security/Security.h>

SWIFT_RUNTIME_STDLIB_INTERNAL
void swift::_stdlib_random(void *buf, __swift_size_t nbytes) {
if (__builtin_available(macOS 10.12, iOS 10, tvOS 10, watchOS 3, *)) {
Copy link

@xwu xwu Feb 7, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of curiosity, why isn't this conditional written in the form of a macro? And why not just stick with SecRandomCopyBytes for all Apple platforms?

(I recognize that this isn't being changed here, but I thought I'd ask.)

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea so Greg Parker brought this up as well in the actual pr, but I know a few on the mailing list had said that the Darwin solution should use arc4random. In addition to those comments, the whole family of arc4random is advertised as being always successful on the mac man pages man arc4random, so removing the possibility of error in something I'm in favor of. Although, I do see using one solution for all versions as an easier implementation.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of curiosity, why isn't this conditional written in the form of a macro?

@xwu The original was using macros. I suggested __builtin_available because it should be a runtime conditional. AFAIK, until the stdlib is a system library, MAC_OS_X_VERSION_MIN_REQUIRED will always be MAC_OS_X_VERSION_10_9 (see supported target platforms).

arc4random_buf(buf, nbytes);
}else {
} else {
OSStatus status = SecRandomCopyBytes(kSecRandomDefault, nbytes, buf);
if (status != errSecSuccess) {
fatalError(0, "Fatal error: SecRandomCopyBytes failed with error %d\n", status);
fatalError(0, "Fatal error: %d in '%s'\n", status, __func__);
}
}
}
#elif defined(__linux__)
#include <linux/version.h>
#include <sys/syscall.h>

#elif defined(_WIN32) && !defined(__CYGWIN__)
#error TODO: link with Bcrypt.lib and test on Windows.

SWIFT_RUNTIME_STDLIB_INTERNAL
void swift::_stdlib_random(void *buf, __swift_size_t nbytes) {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,17,0)
auto _read = [&]() -> __swift_ssize_t {
return syscall(SYS_getrandom, buf, bytes, 0);
};
#else
auto _read = [&]() -> __swift_ssize_t {
static const int fd = _stdlib_open("/dev/urandom", O_RDONLY);
if (fd < 0) {
fatalError(0, "Unable to open '/dev/urandom'\n");
NTSTATUS status = BCryptGenRandom(nullptr,
static_cast<PUCHAR>(buf),
static_cast<ULONG>(nbytes),
BCRYPT_USE_SYSTEM_PREFERRED_RNG);
if (!NT_SUCCESS(status)) {
fatalError(0, "Fatal error: %#.8x in '%s'\n", status, __func__);
}
}

#elif defined(SWIFT_STDLIB_USING_GETENTROPY)

SWIFT_RUNTIME_STDLIB_INTERNAL
void swift::_stdlib_random(void *buf, __swift_size_t nbytes) {
while (nbytes > 0) {
constexpr __swift_size_t max_nbytes = 256;
__swift_size_t actual_nbytes = (nbytes < max_nbytes ?
nbytes : max_nbytes);
if (0 != getentropy(buf, actual_nbytes)) {
fatalError(0, "Fatal error: %d in '%s'\n", errno, __func__);
}
return _stdlib_read(fd, buf, bytes);
};
buf = static_cast<uint8_t *>(buf) + actual_nbytes;
nbytes -= actual_nbytes;
}
}

#else

SWIFT_RUNTIME_STDLIB_INTERNAL
void swift::_stdlib_random(void *buf, __swift_size_t nbytes) {
#if !defined(SWIFT_STDLIB_USING_GETRANDOM)
static const int fd = _stdlib_open("/dev/urandom", O_RDONLY, 0);
if (fd < 0) {
fatalError(0, "Fatal error: %d in '%s'\n", errno, __func__);
}
#endif
while (nbytes > 0) {
auto result = _read();
if (result < 1) {
#if !defined(SWIFT_STDLIB_USING_GETRANDOM)
__swift_ssize_t actual_nbytes = _stdlib_read(fd, buf, nbytes);
#else
__swift_ssize_t actual_nbytes = getrandom(buf, nbytes, 0);
#endif
if (actual_nbytes < 1) {
if (errno == EINTR) { continue; }
fatalError(0, "Unable to read '/dev/urandom'\n");
fatalError(0, "Fatal error: %d in '%s'\n", errno, __func__);
}
buf = static_cast<uint8_t *>(buf) + result;
nbytes -= result;
buf = static_cast<uint8_t *>(buf) + actual_nbytes;
nbytes -= actual_nbytes;
}
}

#endif