#include "base/rand_util.h"
#include <errno.h>
#include <fcntl.h>
#include <stddef.h>
#include <stdint.h>
#include <sys/syscall.h>
#include <sys/utsname.h>
#include <unistd.h>
#include "base/check.h"
#include "base/compiler_specific.h"
#include "base/feature_list.h"
#include "base/files/file_util.h"
#include "base/metrics/histogram_macros.h"
#include "base/no_destructor.h"
#include "base/posix/eintr_wrapper.h"
#include "base/time/time.h"
#include "build/build_config.h"
#if (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)) && !BUILDFLAG(IS_NACL)
#include "third_party/lss/linux_syscall_support.h"
#elif BUILDFLAG(IS_MAC)
#include <sys/random.h>
#endif
#if !BUILDFLAG(IS_NACL)
#include "third_party/boringssl/src/include/openssl/crypto.h"
#include "third_party/boringssl/src/include/openssl/rand.h"
#endif
namespace base {
namespace {
#if BUILDFLAG(IS_AIX)
static constexpr int kOpenFlags = O_RDONLY;
#else
static constexpr int kOpenFlags = O_RDONLY | O_CLOEXEC;
#endif
class URandomFd {
public:
URandomFd() : fd_(HANDLE_EINTR(open("/dev/urandom", kOpenFlags))) {
CHECK(fd_ >= 0) << "Cannot open /dev/urandom";
}
~URandomFd() { close(fd_); }
int fd() const { return fd_; }
private:
const int fd_;
};
#if (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || \
BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_OHOS)) && \
!BUILDFLAG(IS_NACL)
void KernelVersionNumbers(int32_t* major_version,
int32_t* minor_version,
int32_t* bugfix_version,
bool *is_linux) {
struct utsname info;
if (uname(&info) < 0) {
NOTREACHED();
*major_version = 0;
*minor_version = 0;
*bugfix_version = 0;
return;
}
if ("Linux" != std::string(info.sysname)) {
*is_linux = false;
return;
}
*is_linux = true;
int num_read = sscanf(info.release, "%d.%d.%d", major_version, minor_version,
bugfix_version);
if (num_read < 1)
*major_version = 0;
if (num_read < 2)
*minor_version = 0;
if (num_read < 3)
*bugfix_version = 0;
}
bool KernelSupportsGetRandom() {
int32_t major = 0;
int32_t minor = 0;
int32_t bugfix = 0;
bool is_linux = true;
KernelVersionNumbers(&major, &minor, &bugfix, &is_linux);
if (!is_linux) {
return true;
}
if (major > 3 || (major == 3 && minor >= 17))
return true;
LOG(WARNING) << "linux kernel version is too olf, don't support getrandom syscall";
return false;
}
bool GetRandomSyscall(void* output, size_t output_length) {
const ssize_t r =
HANDLE_EINTR(syscall(__NR_getrandom, output, output_length, 0));
if (output_length == static_cast<size_t>(r)) {
MSAN_UNPOISON(output, output_length);
return true;
}
LOG(WARNING) << "getrandom syscall failed, ret = " << r << ", output len = " \
<< output_length << ", output addr = " << output << ", errno = " << errno;
return false;
}
#endif
#if BUILDFLAG(IS_ANDROID)
std::atomic<bool> g_use_getrandom;
BASE_FEATURE(kUseGetrandomForRandBytes,
"UseGetrandomForRandBytes",
FEATURE_ENABLED_BY_DEFAULT);
bool UseGetrandom() {
return g_use_getrandom.load(std::memory_order_relaxed);
}
#elif (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_OHOS)) && !BUILDFLAG(IS_NACL)
bool UseGetrandom() {
return true;
}
#endif
}
namespace internal {
#if BUILDFLAG(IS_ANDROID)
void ConfigureRandBytesFieldTrial() {
g_use_getrandom.store(FeatureList::IsEnabled(kUseGetrandomForRandBytes),
std::memory_order_relaxed);
}
#endif
namespace {
#if !BUILDFLAG(IS_NACL)
std::atomic<bool> g_use_boringssl;
BASE_FEATURE(kUseBoringSSLForRandBytes,
"UseBoringSSLForRandBytes",
FEATURE_DISABLED_BY_DEFAULT);
}
void ConfigureBoringSSLBackedRandBytesFieldTrial() {
g_use_boringssl.store(FeatureList::IsEnabled(kUseBoringSSLForRandBytes),
std::memory_order_relaxed);
}
bool UseBoringSSLForRandBytes() {
return g_use_boringssl.load(std::memory_order_relaxed);
}
#endif
}
namespace {
void RandBytes(void* output, size_t output_length, bool avoid_allocation) {
#if !BUILDFLAG(IS_NACL)
if (!avoid_allocation && internal::UseBoringSSLForRandBytes()) {
CRYPTO_library_init();
(void)RAND_bytes(static_cast<uint8_t*>(output), output_length);
return;
}
#endif
#if (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || \
BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_OHOS)) && !BUILDFLAG(IS_NACL)
if (avoid_allocation || UseGetrandom()) {
static const bool kernel_has_support = KernelSupportsGetRandom();
if (kernel_has_support && GetRandomSyscall(output, output_length))
return;
}
#elif BUILDFLAG(IS_MAC)
if (getentropy(output, output_length) == 0) {
return;
}
#endif
LOG(WARNING) << "getrandom syscall failed, fall through to reading from urandom";
const int urandom_fd = GetUrandomFD();
const bool success =
ReadFromFD(urandom_fd, static_cast<char*>(output), output_length);
CHECK(success);
}
}
namespace internal {
double RandDoubleAvoidAllocation() {
uint64_t number;
RandBytes(&number, sizeof(number), true);
return (number >> 11) * 0x1.0p-53;
}
}
void RandBytes(void* output, size_t output_length) {
RandBytes(output, output_length, false);
}
int GetUrandomFD() {
static NoDestructor<URandomFd> urandom_fd;
return urandom_fd->fd();
}
}