diff --git a/Source/WTF/wtf/RandomDevice.cpp b/Source/WTF/wtf/RandomDevice.cpp index 95921fd376ded..a088a8ce3bebf 100644 --- a/Source/WTF/wtf/RandomDevice.cpp +++ b/Source/WTF/wtf/RandomDevice.cpp @@ -35,6 +35,19 @@ #include #endif +#if OS(LINUX) +#include +#if defined(SYS_getrandom) +#define RANDOM_DEVICE_USE_GETRANDOM 1 +#ifndef GRND_NONBLOCK +#define GRND_NONBLOCK 0x0001 +#endif +#endif +#endif +#ifndef RANDOM_DEVICE_USE_GETRANDOM +#define RANDOM_DEVICE_USE_GETRANDOM 0 +#endif + #if OS(WINDOWS) #include #include // windows.h must be included before wincrypt.h. @@ -66,9 +79,24 @@ NEVER_INLINE NO_RETURN_DUE_TO_CRASH static void crashUnableToReadFromURandom() #if !OS(DARWIN) && !OS(FUCHSIA) && !OS(WINDOWS) RandomDevice::RandomDevice() { +#if RANDOM_DEVICE_USE_GETRANDOM + // Prefer getrandom(2): same kernel pool as /dev/urandom, but needs no + // filesystem access and keeps no fd open. Probe once with GRND_NONBLOCK; if + // the pool is already initialized use getrandom from here on (it can never + // become uninitialized again). Otherwise -- EAGAIN (pool not ready, where + // /dev/urandom would return without blocking), ENOSYS (pre-3.17 kernel) or + // EPERM (seccomp) -- keep the historical /dev/urandom behaviour. + uint8_t probe; + long probeResult; + do { + probeResult = syscall(SYS_getrandom, &probe, sizeof(probe), GRND_NONBLOCK); + } while (probeResult == -1 && errno == EINTR); + if (probeResult == 1) + return; // m_fd stays -1: cryptographicallyRandomValues() uses getrandom. +#endif int ret = 0; do { - ret = open("/dev/urandom", O_RDONLY, 0); + ret = open("/dev/urandom", O_RDONLY | O_CLOEXEC, 0); } while (ret == -1 && errno == EINTR); m_fd = ret; if (m_fd < 0) @@ -79,7 +107,8 @@ RandomDevice::RandomDevice() #if !OS(DARWIN) && !OS(FUCHSIA) && !OS(WINDOWS) RandomDevice::~RandomDevice() { - close(m_fd); + if (m_fd >= 0) + close(m_fd); } #endif @@ -94,8 +123,14 @@ void RandomDevice::cryptographicallyRandomValues(std::span buffer) #elif OS(UNIX) ssize_t amountRead = 0; while (static_cast(amountRead) < buffer.size()) { + ssize_t currentRead; WTF_ALLOW_UNSAFE_BUFFER_USAGE_BEGIN - ssize_t currentRead = read(m_fd, buffer.data() + amountRead, buffer.size() - amountRead); +#if RANDOM_DEVICE_USE_GETRANDOM + if (m_fd < 0) + currentRead = syscall(SYS_getrandom, buffer.data() + amountRead, buffer.size() - amountRead, 0); + else +#endif + currentRead = read(m_fd, buffer.data() + amountRead, buffer.size() - amountRead); WTF_ALLOW_UNSAFE_BUFFER_USAGE_END // We need to check for both EAGAIN and EINTR since on some systems /dev/urandom // is blocking and on others it is non-blocking. diff --git a/Source/WTF/wtf/linux/CurrentProcessMemoryStatus.cpp b/Source/WTF/wtf/linux/CurrentProcessMemoryStatus.cpp index 9dd80c241105b..69342a17c586e 100644 --- a/Source/WTF/wtf/linux/CurrentProcessMemoryStatus.cpp +++ b/Source/WTF/wtf/linux/CurrentProcessMemoryStatus.cpp @@ -26,29 +26,43 @@ #include "config.h" #include -#include +#include +#include +#include #include #include #include namespace WTF { -IGNORE_CLANG_WARNINGS_BEGIN("unsafe-buffer-usage-in-libc-call") +WTF_ALLOW_UNSAFE_BUFFER_USAGE_BEGIN void currentProcessMemoryStatus(ProcessMemoryStatus& memoryStatus) { - FILE* file = fopen("/proc/self/statm", "r"); - if (!file) + // This is on the GC's heap-growth path (Heap::proportionalHeapSize via + // memoryFootprint()), so open /proc/self/statm once and pread() it rather + // than fopen/fclose on every call. If it can't be opened, latch to "no data". + static int statmFd = -1; + static std::once_flag onceFlag; + std::call_once(onceFlag, [] { + do { + statmFd = open("/proc/self/statm", O_RDONLY | O_CLOEXEC); + } while (statmFd == -1 && errno == EINTR); + }); + if (statmFd < 0) return; char buffer[128]; - char* line = fgets(buffer, 128, file); - fclose(file); - if (!line) + ssize_t length; + do { + length = pread(statmFd, buffer, sizeof(buffer) - 1, 0); + } while (length == -1 && errno == EINTR); + if (length <= 0) return; + buffer[length] = '\0'; size_t pageSize = WTF::pageSize(); char* end = nullptr; - unsigned long long intValue = strtoull(line, &end, 10); + unsigned long long intValue = strtoull(buffer, &end, 10); memoryStatus.size = intValue * pageSize; intValue = strtoull(end, &end, 10); memoryStatus.resident = intValue * pageSize; @@ -63,6 +77,6 @@ void currentProcessMemoryStatus(ProcessMemoryStatus& memoryStatus) intValue = strtoull(end, &end, 10); memoryStatus.dt = intValue * pageSize; } -IGNORE_CLANG_WARNINGS_END +WTF_ALLOW_UNSAFE_BUFFER_USAGE_END } // namespace WTF diff --git a/Source/bmalloc/bmalloc/CryptoRandom.cpp b/Source/bmalloc/bmalloc/CryptoRandom.cpp index 4724cad68f1bd..d0ca5b2b338f3 100644 --- a/Source/bmalloc/bmalloc/CryptoRandom.cpp +++ b/Source/bmalloc/bmalloc/CryptoRandom.cpp @@ -43,6 +43,19 @@ #include #endif +#if BOS(LINUX) +#include +#if defined(SYS_getrandom) +#define CRYPTO_RANDOM_USE_GETRANDOM 1 +#ifndef GRND_NONBLOCK +#define GRND_NONBLOCK 0x0001 +#endif +#endif +#endif +#ifndef CRYPTO_RANDOM_USE_GETRANDOM +#define CRYPTO_RANDOM_USE_GETRANDOM 0 +#endif + #if BOS(DARWIN) #include #include @@ -118,20 +131,41 @@ void ARC4RandomNumberGenerator::stir() BCRASH(); #else static std::once_flag onceFlag; - static int fd; + static int fd = -1; std::call_once( onceFlag, [] { +#if CRYPTO_RANDOM_USE_GETRANDOM + // Prefer getrandom(2): same kernel pool as /dev/urandom, but needs no + // filesystem access and keeps no fd open. Probe once with GRND_NONBLOCK; + // if the pool is already initialized use getrandom from here on (it can + // never become uninitialized again). Otherwise -- EAGAIN (pool not ready, + // where /dev/urandom would return without blocking), ENOSYS (pre-3.17 + // kernel) or EPERM (seccomp) -- keep the historical /dev/urandom behaviour. + uint8_t probe; + long probeResult; + do { + probeResult = syscall(SYS_getrandom, &probe, sizeof(probe), GRND_NONBLOCK); + } while (probeResult == -1 && errno == EINTR); + if (probeResult == 1) + return; // fd stays -1: use getrandom below. +#endif int ret = 0; do { - ret = open("/dev/urandom", O_RDONLY, 0); + ret = open("/dev/urandom", O_RDONLY | O_CLOEXEC, 0); } while (ret == -1 && errno == EINTR); RELEASE_BASSERT(ret >= 0); fd = ret; }); ssize_t amountRead = 0; while (static_cast(amountRead) < length) { - ssize_t currentRead = read(fd, randomness + amountRead, length - amountRead); + ssize_t currentRead; +#if CRYPTO_RANDOM_USE_GETRANDOM + if (fd < 0) + currentRead = syscall(SYS_getrandom, randomness + amountRead, length - amountRead, 0); + else +#endif + currentRead = read(fd, randomness + amountRead, length - amountRead); // We need to check for both EAGAIN and EINTR since on some systems /dev/urandom // is blocking and on others it is non-blocking. if (currentRead == -1)