mirror of
https://github.com/microsoft/mimalloc.git
synced 2025-05-06 07:29:30 +03:00
move random initialization to primitives
This commit is contained in:
parent
9b110090b2
commit
973268bf1e
5 changed files with 150 additions and 159 deletions
|
@ -6,7 +6,7 @@ terms of the MIT license. A copy of the license can be found in the file
|
||||||
-----------------------------------------------------------------------------*/
|
-----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
#ifndef _DEFAULT_SOURCE
|
#ifndef _DEFAULT_SOURCE
|
||||||
#define _DEFAULT_SOURCE // ensure mmap flags are defined
|
#define _DEFAULT_SOURCE // ensure mmap flags and syscall are defined
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(__sun)
|
#if defined(__sun)
|
||||||
|
@ -661,3 +661,91 @@ bool _mi_prim_getenv(const char* name, char* result, size_t result_size) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
#endif // !MI_USE_ENVIRON
|
#endif // !MI_USE_ENVIRON
|
||||||
|
|
||||||
|
|
||||||
|
//----------------------------------------------------------------
|
||||||
|
// Random
|
||||||
|
//----------------------------------------------------------------
|
||||||
|
|
||||||
|
#if defined(__APPLE__)
|
||||||
|
|
||||||
|
#include <AvailabilityMacros.h>
|
||||||
|
#if defined(MAC_OS_X_VERSION_10_10) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_10
|
||||||
|
#include <CommonCrypto/CommonCryptoError.h>
|
||||||
|
#include <CommonCrypto/CommonRandom.h>
|
||||||
|
#endif
|
||||||
|
bool _mi_prim_random_buf(void* buf, size_t buf_len) {
|
||||||
|
#if defined(MAC_OS_X_VERSION_10_15) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_15
|
||||||
|
// We prefere CCRandomGenerateBytes as it returns an error code while arc4random_buf
|
||||||
|
// may fail silently on macOS. See PR #390, and <https://opensource.apple.com/source/Libc/Libc-1439.40.11/gen/FreeBSD/arc4random.c.auto.html>
|
||||||
|
return (CCRandomGenerateBytes(buf, buf_len) == kCCSuccess);
|
||||||
|
#else
|
||||||
|
// fall back on older macOS
|
||||||
|
arc4random_buf(buf, buf_len);
|
||||||
|
return true;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(__ANDROID__) || defined(__DragonFly__) || \
|
||||||
|
defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || \
|
||||||
|
defined(__sun)
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
bool _mi_prim_random_buf(void* buf, size_t buf_len) {
|
||||||
|
arc4random_buf(buf, buf_len);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(__linux__) || defined(__HAIKU__)
|
||||||
|
|
||||||
|
#if defined(__linux__)
|
||||||
|
#include <sys/syscall.h>
|
||||||
|
#endif
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <errno.h>
|
||||||
|
bool _mi_prim_random_buf(void* buf, size_t buf_len) {
|
||||||
|
// Modern Linux provides `getrandom` but different distributions either use `sys/random.h` or `linux/random.h`
|
||||||
|
// and for the latter the actual `getrandom` call is not always defined.
|
||||||
|
// (see <https://stackoverflow.com/questions/45237324/why-doesnt-getrandom-compile>)
|
||||||
|
// We therefore use a syscall directly and fall back dynamically to /dev/urandom when needed.
|
||||||
|
#ifdef SYS_getrandom
|
||||||
|
#ifndef GRND_NONBLOCK
|
||||||
|
#define GRND_NONBLOCK (1)
|
||||||
|
#endif
|
||||||
|
static _Atomic(uintptr_t) no_getrandom; // = 0
|
||||||
|
if (mi_atomic_load_acquire(&no_getrandom)==0) {
|
||||||
|
ssize_t ret = syscall(SYS_getrandom, buf, buf_len, GRND_NONBLOCK);
|
||||||
|
if (ret >= 0) return (buf_len == (size_t)ret);
|
||||||
|
if (errno != ENOSYS) return false;
|
||||||
|
mi_atomic_store_release(&no_getrandom, 1UL); // don't call again, and fall back to /dev/urandom
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
int flags = O_RDONLY;
|
||||||
|
#if defined(O_CLOEXEC)
|
||||||
|
flags |= O_CLOEXEC;
|
||||||
|
#endif
|
||||||
|
int fd = open("/dev/urandom", flags, 0);
|
||||||
|
if (fd < 0) return false;
|
||||||
|
size_t count = 0;
|
||||||
|
while(count < buf_len) {
|
||||||
|
ssize_t ret = read(fd, (char*)buf + count, buf_len - count);
|
||||||
|
if (ret<=0) {
|
||||||
|
if (errno!=EAGAIN && errno!=EINTR) break;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
count += ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
close(fd);
|
||||||
|
return (count==buf_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
bool _mi_prim_random_buf(void* buf, size_t buf_len) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -234,3 +234,12 @@ bool _mi_prim_getenv(const char* name, char* result, size_t result_size) {
|
||||||
_mi_strlcpy(result, s, result_size);
|
_mi_strlcpy(result, s, result_size);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//----------------------------------------------------------------
|
||||||
|
// Random
|
||||||
|
//----------------------------------------------------------------
|
||||||
|
|
||||||
|
bool _mi_prim_random_buf(void* buf, size_t buf_len) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
|
@ -506,3 +506,45 @@ bool _mi_prim_getenv(const char* name, char* result, size_t result_size) {
|
||||||
size_t len = GetEnvironmentVariableA(name, result, (DWORD)result_size);
|
size_t len = GetEnvironmentVariableA(name, result, (DWORD)result_size);
|
||||||
return (len > 0 && len < result_size);
|
return (len > 0 && len < result_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//----------------------------------------------------------------
|
||||||
|
// Random
|
||||||
|
//----------------------------------------------------------------
|
||||||
|
|
||||||
|
#if defined(MI_USE_RTLGENRANDOM) // || defined(__cplusplus)
|
||||||
|
// We prefer to use BCryptGenRandom instead of (the unofficial) RtlGenRandom but when using
|
||||||
|
// dynamic overriding, we observed it can raise an exception when compiled with C++, and
|
||||||
|
// sometimes deadlocks when also running under the VS debugger.
|
||||||
|
// In contrast, issue #623 implies that on Windows Server 2019 we need to use BCryptGenRandom.
|
||||||
|
// To be continued..
|
||||||
|
#pragma comment (lib,"advapi32.lib")
|
||||||
|
#define RtlGenRandom SystemFunction036
|
||||||
|
mi_decl_externc BOOLEAN NTAPI RtlGenRandom(PVOID RandomBuffer, ULONG RandomBufferLength);
|
||||||
|
|
||||||
|
bool _mi_prim_random_buf(void* buf, size_t buf_len) {
|
||||||
|
return (RtlGenRandom(buf, (ULONG)buf_len) != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#ifndef BCRYPT_USE_SYSTEM_PREFERRED_RNG
|
||||||
|
#define BCRYPT_USE_SYSTEM_PREFERRED_RNG 0x00000002
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef LONG (NTAPI *PBCryptGenRandom)(HANDLE, PUCHAR, ULONG, ULONG);
|
||||||
|
static PBCryptGenRandom pBCryptGenRandom = NULL;
|
||||||
|
|
||||||
|
bool _mi_prim_random_buf(void* buf, size_t buf_len) {
|
||||||
|
if (pBCryptGenRandom == NULL) {
|
||||||
|
HINSTANCE hDll = LoadLibrary(TEXT("bcrypt.dll"));
|
||||||
|
if (hDll != NULL) {
|
||||||
|
pBCryptGenRandom = (PBCryptGenRandom)(void (*)(void))GetProcAddress(hDll, "BCryptGenRandom");
|
||||||
|
}
|
||||||
|
if (pBCryptGenRandom == NULL) return false;
|
||||||
|
}
|
||||||
|
return (pBCryptGenRandom(NULL, (PUCHAR)buf, (ULONG)buf_len, BCRYPT_USE_SYSTEM_PREFERRED_RNG) >= 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // MI_USE_RTLGENRANDOM
|
||||||
|
|
|
@ -72,6 +72,10 @@ void _mi_prim_out_stderr( const char* msg );
|
||||||
bool _mi_prim_getenv(const char* name, char* result, size_t result_size);
|
bool _mi_prim_getenv(const char* name, char* result, size_t result_size);
|
||||||
|
|
||||||
|
|
||||||
|
// Fill a buffer with strong randomness; return `false` on error or if
|
||||||
|
// there is no strong randomization available.
|
||||||
|
bool _mi_prim_random_buf(void* buf, size_t buf_len);
|
||||||
|
|
||||||
//-------------------------------------------------------------------
|
//-------------------------------------------------------------------
|
||||||
// Thread id
|
// Thread id
|
||||||
//
|
//
|
||||||
|
|
160
src/random.c
160
src/random.c
|
@ -4,13 +4,9 @@ This is free software; you can redistribute it and/or modify it under the
|
||||||
terms of the MIT license. A copy of the license can be found in the file
|
terms of the MIT license. A copy of the license can be found in the file
|
||||||
"LICENSE" at the root of this distribution.
|
"LICENSE" at the root of this distribution.
|
||||||
-----------------------------------------------------------------------------*/
|
-----------------------------------------------------------------------------*/
|
||||||
#ifndef _DEFAULT_SOURCE
|
|
||||||
#define _DEFAULT_SOURCE // for syscall() on Linux
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "mimalloc.h"
|
#include "mimalloc.h"
|
||||||
#include "mimalloc-internal.h"
|
#include "mimalloc-internal.h"
|
||||||
|
#include "prim/prim.h" // _mi_prim_random_buf
|
||||||
#include <string.h> // memset
|
#include <string.h> // memset
|
||||||
|
|
||||||
/* ----------------------------------------------------------------------------
|
/* ----------------------------------------------------------------------------
|
||||||
|
@ -158,161 +154,13 @@ uintptr_t _mi_random_next(mi_random_ctx_t* ctx) {
|
||||||
|
|
||||||
|
|
||||||
/* ----------------------------------------------------------------------------
|
/* ----------------------------------------------------------------------------
|
||||||
To initialize a fresh random context we rely on the OS:
|
To initialize a fresh random context.
|
||||||
- Windows : BCryptGenRandom (or RtlGenRandom)
|
|
||||||
- macOS : CCRandomGenerateBytes, arc4random_buf
|
|
||||||
- bsd,wasi : arc4random_buf
|
|
||||||
- Linux : getrandom,/dev/urandom
|
|
||||||
If we cannot get good randomness, we fall back to weak randomness based on a timer and ASLR.
|
If we cannot get good randomness, we fall back to weak randomness based on a timer and ASLR.
|
||||||
-----------------------------------------------------------------------------*/
|
-----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
#if defined(_WIN32)
|
|
||||||
|
|
||||||
#include <windows.h>
|
|
||||||
|
|
||||||
#if defined(MI_USE_RTLGENRANDOM) // || defined(__cplusplus)
|
|
||||||
// We prefer to use BCryptGenRandom instead of (the unofficial) RtlGenRandom but when using
|
|
||||||
// dynamic overriding, we observed it can raise an exception when compiled with C++, and
|
|
||||||
// sometimes deadlocks when also running under the VS debugger.
|
|
||||||
// In contrast, issue #623 implies that on Windows Server 2019 we need to use BCryptGenRandom.
|
|
||||||
// To be continued..
|
|
||||||
#pragma comment (lib,"advapi32.lib")
|
|
||||||
#define RtlGenRandom SystemFunction036
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
BOOLEAN NTAPI RtlGenRandom(PVOID RandomBuffer, ULONG RandomBufferLength);
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
static bool os_random_buf(void* buf, size_t buf_len) {
|
|
||||||
return (RtlGenRandom(buf, (ULONG)buf_len) != 0);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
|
|
||||||
#ifndef BCRYPT_USE_SYSTEM_PREFERRED_RNG
|
|
||||||
#define BCRYPT_USE_SYSTEM_PREFERRED_RNG 0x00000002
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef LONG (NTAPI *PBCryptGenRandom)(HANDLE, PUCHAR, ULONG, ULONG);
|
|
||||||
static PBCryptGenRandom pBCryptGenRandom = NULL;
|
|
||||||
|
|
||||||
static bool os_random_buf(void* buf, size_t buf_len) {
|
|
||||||
if (pBCryptGenRandom == NULL) {
|
|
||||||
HINSTANCE hDll = LoadLibrary(TEXT("bcrypt.dll"));
|
|
||||||
if (hDll != NULL) {
|
|
||||||
pBCryptGenRandom = (PBCryptGenRandom)(void (*)(void))GetProcAddress(hDll, "BCryptGenRandom");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (pBCryptGenRandom == NULL) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return (pBCryptGenRandom(NULL, (PUCHAR)buf, (ULONG)buf_len, BCRYPT_USE_SYSTEM_PREFERRED_RNG) >= 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#elif defined(__APPLE__)
|
|
||||||
#include <AvailabilityMacros.h>
|
|
||||||
#if defined(MAC_OS_X_VERSION_10_10) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_10
|
|
||||||
#include <CommonCrypto/CommonCryptoError.h>
|
|
||||||
#include <CommonCrypto/CommonRandom.h>
|
|
||||||
#endif
|
|
||||||
static bool os_random_buf(void* buf, size_t buf_len) {
|
|
||||||
#if defined(MAC_OS_X_VERSION_10_15) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_15
|
|
||||||
// We prefere CCRandomGenerateBytes as it returns an error code while arc4random_buf
|
|
||||||
// may fail silently on macOS. See PR #390, and <https://opensource.apple.com/source/Libc/Libc-1439.40.11/gen/FreeBSD/arc4random.c.auto.html>
|
|
||||||
return (CCRandomGenerateBytes(buf, buf_len) == kCCSuccess);
|
|
||||||
#else
|
|
||||||
// fall back on older macOS
|
|
||||||
arc4random_buf(buf, buf_len);
|
|
||||||
return true;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
#elif defined(__ANDROID__) || defined(__DragonFly__) || \
|
|
||||||
defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || \
|
|
||||||
defined(__sun) // todo: what to use with __wasi__?
|
|
||||||
#include <stdlib.h>
|
|
||||||
static bool os_random_buf(void* buf, size_t buf_len) {
|
|
||||||
arc4random_buf(buf, buf_len);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
#elif defined(__linux__) || defined(__HAIKU__)
|
|
||||||
#if defined(__linux__)
|
|
||||||
#include <sys/syscall.h>
|
|
||||||
#endif
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <errno.h>
|
|
||||||
static bool os_random_buf(void* buf, size_t buf_len) {
|
|
||||||
// Modern Linux provides `getrandom` but different distributions either use `sys/random.h` or `linux/random.h`
|
|
||||||
// and for the latter the actual `getrandom` call is not always defined.
|
|
||||||
// (see <https://stackoverflow.com/questions/45237324/why-doesnt-getrandom-compile>)
|
|
||||||
// We therefore use a syscall directly and fall back dynamically to /dev/urandom when needed.
|
|
||||||
#ifdef SYS_getrandom
|
|
||||||
#ifndef GRND_NONBLOCK
|
|
||||||
#define GRND_NONBLOCK (1)
|
|
||||||
#endif
|
|
||||||
static _Atomic(uintptr_t) no_getrandom; // = 0
|
|
||||||
if (mi_atomic_load_acquire(&no_getrandom)==0) {
|
|
||||||
ssize_t ret = syscall(SYS_getrandom, buf, buf_len, GRND_NONBLOCK);
|
|
||||||
if (ret >= 0) return (buf_len == (size_t)ret);
|
|
||||||
if (errno != ENOSYS) return false;
|
|
||||||
mi_atomic_store_release(&no_getrandom, 1UL); // don't call again, and fall back to /dev/urandom
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
int flags = O_RDONLY;
|
|
||||||
#if defined(O_CLOEXEC)
|
|
||||||
flags |= O_CLOEXEC;
|
|
||||||
#endif
|
|
||||||
int fd = open("/dev/urandom", flags, 0);
|
|
||||||
if (fd < 0) return false;
|
|
||||||
size_t count = 0;
|
|
||||||
while(count < buf_len) {
|
|
||||||
ssize_t ret = read(fd, (char*)buf + count, buf_len - count);
|
|
||||||
if (ret<=0) {
|
|
||||||
if (errno!=EAGAIN && errno!=EINTR) break;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
count += ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
close(fd);
|
|
||||||
return (count==buf_len);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
static bool os_random_buf(void* buf, size_t buf_len) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(_WIN32)
|
|
||||||
#include <windows.h>
|
|
||||||
#elif defined(__APPLE__)
|
|
||||||
#include <mach/mach_time.h>
|
|
||||||
#else
|
|
||||||
#include <time.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
uintptr_t _mi_os_random_weak(uintptr_t extra_seed) {
|
uintptr_t _mi_os_random_weak(uintptr_t extra_seed) {
|
||||||
uintptr_t x = (uintptr_t)&_mi_os_random_weak ^ extra_seed; // ASLR makes the address random
|
uintptr_t x = (uintptr_t)&_mi_os_random_weak ^ extra_seed; // ASLR makes the address random
|
||||||
|
x ^= _mi_prim_clock_now();
|
||||||
#if defined(_WIN32)
|
|
||||||
LARGE_INTEGER pcount;
|
|
||||||
QueryPerformanceCounter(&pcount);
|
|
||||||
x ^= (uintptr_t)(pcount.QuadPart);
|
|
||||||
#elif defined(__APPLE__)
|
|
||||||
x ^= (uintptr_t)mach_absolute_time();
|
|
||||||
#else
|
|
||||||
struct timespec time;
|
|
||||||
clock_gettime(CLOCK_MONOTONIC, &time);
|
|
||||||
x ^= (uintptr_t)time.tv_sec;
|
|
||||||
x ^= (uintptr_t)time.tv_nsec;
|
|
||||||
#endif
|
|
||||||
// and do a few randomization steps
|
// and do a few randomization steps
|
||||||
uintptr_t max = ((x ^ (x >> 17)) & 0x0F) + 1;
|
uintptr_t max = ((x ^ (x >> 17)) & 0x0F) + 1;
|
||||||
for (uintptr_t i = 0; i < max; i++) {
|
for (uintptr_t i = 0; i < max; i++) {
|
||||||
|
@ -324,7 +172,7 @@ uintptr_t _mi_os_random_weak(uintptr_t extra_seed) {
|
||||||
|
|
||||||
static void mi_random_init_ex(mi_random_ctx_t* ctx, bool use_weak) {
|
static void mi_random_init_ex(mi_random_ctx_t* ctx, bool use_weak) {
|
||||||
uint8_t key[32];
|
uint8_t key[32];
|
||||||
if (use_weak || !os_random_buf(key, sizeof(key))) {
|
if (use_weak || !_mi_prim_random_buf(key, sizeof(key))) {
|
||||||
// if we fail to get random data from the OS, we fall back to a
|
// if we fail to get random data from the OS, we fall back to a
|
||||||
// weak random source based on the current time
|
// weak random source based on the current time
|
||||||
#if !defined(__wasi__)
|
#if !defined(__wasi__)
|
||||||
|
|
Loading…
Add table
Reference in a new issue