blob: fee0c311e679e5832fd0cdff39c643439594b4c6 [file] [log] [blame]
//===--- Random.cpp -------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
// swift_stdlib_random
//
// Should the implementation of this function add a new platform/change for a
// platform, make sure to also update the documentation regarding platform
// implementation of this function.
// This can be found at: /docs/Random.md
#if defined(_WIN32) && !defined(__CYGWIN__)
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <Bcrypt.h>
#pragma comment(lib, "bcrypt.lib")
#else
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#endif
#if __has_include(<sys/random.h>)
#include <sys/random.h>
#endif
#include <sys/stat.h>
#if __has_include(<sys/syscall.h>)
#include <sys/syscall.h>
#endif
#include <stdlib.h>
#include "swift/Runtime/Debug.h"
#include "swift/Runtime/Mutex.h"
#include "../SwiftShims/Random.h"
#if defined(__APPLE__)
SWIFT_RUNTIME_STDLIB_API
void swift::swift_stdlib_random(void *buf, __swift_size_t nbytes) {
arc4random_buf(buf, nbytes);
}
#elif defined(_WIN32) && !defined(__CYGWIN__)
#warning TODO: Test swift_stdlib_random on Windows
SWIFT_RUNTIME_STDLIB_API
void swift::swift_stdlib_random(void *buf, __swift_size_t nbytes) {
NTSTATUS status = BCryptGenRandom(nullptr,
static_cast<PUCHAR>(buf),
static_cast<ULONG>(nbytes),
BCRYPT_USE_SYSTEM_PREFERRED_RNG);
if (!BCRYPT_SUCCESS(status)) {
fatalError(0, "Fatal error: 0x%.8X in '%s'\n", status, __func__);
}
}
#else
#undef WHILE_EINTR
#define WHILE_EINTR(expression) ({ \
decltype(expression) result = -1; \
do { result = (expression); } while (result == -1 && errno == EINTR); \
result; \
})
SWIFT_RUNTIME_STDLIB_API
void swift::swift_stdlib_random(void *buf, __swift_size_t nbytes) {
while (nbytes > 0) {
__swift_ssize_t actual_nbytes = -1;
#if defined(__NR_getrandom)
static const bool getrandom_available =
!(syscall(__NR_getrandom, nullptr, 0, 0) == -1 && errno == ENOSYS);
if (getrandom_available) {
actual_nbytes = WHILE_EINTR(syscall(__NR_getrandom, buf, nbytes, 0));
}
#elif __has_include(<sys/random.h>) && (defined(__CYGWIN__) || defined(__Fuchsia__))
__swift_size_t getentropy_nbytes = std::min(nbytes, __swift_size_t{256});
if (0 == getentropy(buf, getentropy_nbytes)) {
actual_nbytes = getentropy_nbytes;
}
#endif
if (actual_nbytes == -1) {
static const int fd =
WHILE_EINTR(open("/dev/urandom", O_RDONLY | O_CLOEXEC, 0));
if (fd != -1) {
static StaticMutex mutex;
mutex.withLock([&] {
actual_nbytes = WHILE_EINTR(read(fd, buf, nbytes));
});
}
}
if (actual_nbytes == -1) {
fatalError(0, "Fatal error: %d in '%s'\n", errno, __func__);
}
buf = static_cast<uint8_t *>(buf) + actual_nbytes;
nbytes -= actual_nbytes;
}
}
#endif