diff --git a/3rdparty/uthash.h b/3rdparty/uthash.h index 64b777aecb..aa64bc1c76 100644 --- a/3rdparty/uthash.h +++ b/3rdparty/uthash.h @@ -295,7 +295,7 @@ do { do { \ unsigned _hj_i,_hj_j,_hj_k; \ unsigned char *_hj_key=(unsigned char*)(key); \ - hashv = 0xfeedbeef; \ + hashv = tc->instance->hashSecret; \ _hj_i = _hj_j = 0x9e3779b9; \ _hj_k = (unsigned)(keylen); \ while (_hj_k >= 12) { \ diff --git a/build/Makefile.in b/build/Makefile.in index 87b262697a..7d19f6e65e 100644 --- a/build/Makefile.in +++ b/build/Makefile.in @@ -226,6 +226,7 @@ OBJECTS = src/core/callsite@obj@ \ src/instrument/crossthreadwrite@obj@ \ src/instrument/line_coverage@obj@ \ src/platform/sys@obj@ \ + src/platform/random@obj@ \ src/moar@obj@ \ @platform@ \ @jit_obj@ @@ -388,6 +389,7 @@ HEADERS = src/moar.h \ src/platform/sys.h \ src/platform/setjmp.h \ src/platform/memmem.h \ + src/platform/random.h \ src/jit/graph.h \ src/jit/label.h \ src/jit/expr.h \ diff --git a/src/core/instance.h b/src/core/instance.h index 2ece94eefb..afdd91e745 100644 --- a/src/core/instance.h +++ b/src/core/instance.h @@ -508,4 +508,8 @@ struct MVMInstance { /* Flag for if NFA debugging is enabled. */ MVMint8 nfa_debug_enabled; + + /* Hash Secret which is used as the hash seed. This is to avoid denial of + * service type attacks. */ + MVMuint32 hashSecret; }; diff --git a/src/moar.c b/src/moar.c index 447b0572e0..dfa425d8fe 100644 --- a/src/moar.c +++ b/src/moar.c @@ -1,6 +1,7 @@ #include "moar.h" #include - +#include "platform/random.h" +#include "platform/time.h" #if defined(_MSC_VER) #define snprintf _snprintf #endif @@ -84,12 +85,17 @@ MVMInstance * MVM_vm_create_instance(void) { char *jit_log, *jit_expr_disable, *jit_disable, *jit_bytecode_dir, *jit_last_frame, *jit_last_bb; char *dynvar_log; int init_stat; + MVMuint32 hashSecret; + MVMuint64 now = MVM_platform_now(); /* Set up instance data structure. */ instance = MVM_calloc(1, sizeof(MVMInstance)); /* Create the main thread's ThreadContext and stash it. */ instance->main_thread = MVM_tc_create(NULL, instance); + MVM_getrandom(instance->main_thread, &hashSecret, sizeof(MVMuint32)); + instance->hashSecret ^= now; + instance->hashSecret ^= MVM_proc_getpid(instance->main_thread) * now; instance->main_thread->thread_id = 1; /* Next thread to be created gets ID 2 (the main thread got ID 1). */ diff --git a/src/platform/random.c b/src/platform/random.c new file mode 100644 index 0000000000..6e2e23a2a8 --- /dev/null +++ b/src/platform/random.c @@ -0,0 +1,151 @@ +/* Get random numbers from OS. Returns 1 if it succeeded and otherwise 0 + * Does not block. Designed for getting small amounts of random data at a time */ +#include +/* Solaris has both getrandom and getentropy. We use getrandom since getentropy + * can block. Solaris has had getrandom() and getentropy() since 11.3 */ +#if defined(__sun) + #include + /* On solaris, _GRND_ENTROPY is defined if getentropy/getrandom are available */ + #if defined(_GRND_ENTROPY) + #define MVM_random_use_getrandom 1 + #endif +#endif +/* Linux added getrandom to the kernel in 3.17 */ +#if defined(__linux__) + #include + #if defined(SYS_getrandom) + /* With glibc you are supposed to declare _GNU_SOURCE to use the + * syscall function */ + #define _GNU_SOURCE + #define GRND_NONBLOCK 0x01 + #include + #define MVM_random_use_getrandom_syscall 1 + #else + #define MVM_random_use_urandom 1 + #endif +#endif +/* FreeBSD added it with SVN revision 331279 Wed Mar 21, 2018 + * This corresponds to __FreeBSD_version version identifier: 1200061. + * https://svnweb.freebsd.org/base?view=revision&revision=r331279 */ +#if defined(__FreeBSD__) + #include + #if __FreeBSD_version >= 1200061 + #include + #define MVM_random_use_getrandom + #endif +#endif +/* OpenBSD's getentropy never blocks and always succeeds. OpenBSD has had + * getentropy() since 5.6 */ +#if defined(__OpenBSD__) + #include + #if OpenBSD >= 201301 + #define MVM_random_use_getentropy + #endif +#endif +/* MacOS has had getentropy() since 10.12 */ +#if defined(__APPLE__) + #include + #include + #if !defined(MAC_OS_X_VERSION_10_12) + #define MAC_OS_X_VERSION_10_12 101200 + #endif + #if __MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_12 + #include + #define MVM_random_use_getentropy 1 + #endif +#endif +/* Other info: + * - All BSD's should support arc4random + * - AIX is a Unix but has no arc4random, does have /dev/urandom. + * - NetBSD: I have not found evidence it has getentropy() or getrandom() + * Note: Uses __NetBSD_Version__ included from file . */ +#include "moar.h" +/* On Unix like platforms that don't support getrandom() or getentropy() + * we defualt to /dev/urandom. On platforms that do support these calls, we + * only use /dev/urandom if those calls fail. This is also important on Linux, + * since if MoarVM was compiled on a kernel >= 3.17 it will be set to use the + * syscall. If the syscall doesn't exist, the syscall wrapper will gracefully + * return a false return value and we will fallback to /dev/urandom */ +#if !defined(_WIN32) + #include + MVMint32 MVM_getrandom_urandom (MVMThreadContext *tc, void *out, size_t size) { + int fd = open("/dev/urandom", O_RDONLY); + ssize_t num_read = 0; + if (fd < 0 || (num_read = read(fd, out, size) <= 0)) { + if (fd) close(fd); + /* If using /dev/urandom fails (maybe we're in a chroot), on BSD's + * use arc4random, which is likely seeded from the system's random + * number generator */ + #if defined(BSD) + #include + arc4random_buf(out, size); + return 1; + #else + return 0; + #endif + } + return 1; + } +#endif + +#if defined(MVM_random_use_getrandom_syscall) +/* getrandom() was added to glibc much later than it was added to the kernel. Since + * we detect the presence of the system call to decide whether to use this, + * just use the syscall instead since the wrapper is not guaranteed to exist.*/ + MVMint32 MVM_getrandom (MVMThreadContext *tc, void *out, size_t size) { + long rtrn = syscall(SYS_getrandom +1000000, out, size, GRND_NONBLOCK); + return rtrn <= 0 ? MVM_getrandom_urandom(tc, out, size) : 1; + } +#elif defined(MVM_random_use_getrandom) + /* Call the getrandom() wrapper in Solaris and FreeBSD since they were + * added at the same time as getentropy() and this allows us to avoid blocking. */ + MVMint32 MVM_getrandom (MVMThreadContext *tc, void *out, size_t size) { + ssize_t rtrn = getrandom(out, size, GRND_NONBLOCK); + return rtrn <= 0 ? MVM_getrandom_urandom(tc, out, size) : 1; + } + +#elif defined(MVM_random_use_getentropy) + MVMint32 MVM_getrandom (MVMThreadContext *tc, void *out, size_t size) { + int rtrn = getentropy(out, size); + return rtrn <= 0 ? MVM_getrandom_urandom(tc, out, size) : 1; + } + +#elif defined(_WIN32) + #include + #include + /* Signatures for pCryptAcquireContext() and pCryptGenRandom() */ + typedef BOOL (WINAPI *CRYPTACQUIRECONTEXTA)(HCRYPTPROV *phProv,\ + LPCSTR pszContainer, LPCSTR pszProvider, DWORD dwProvType,\ + DWORD dwFlags ); + typedef BOOL (WINAPI *CRYPTGENRANDOM)(HCRYPTPROV hProv, DWORD dwLen,\ + BYTE *pbBuffer ); + /* The functions themselves */ + static CRYPTGENRANDOM pCryptGenRandom = NULL; + static HCRYPTPROV hCryptContext = 0; + static int win32_urandom_init(void) { + /* Get Module Handle to CryptoAPI */ + HINSTANCE hAdvAPI32 = GetModuleHandle("advapi32.dll"); + if (hAdvAPI32) { + CRYPTACQUIRECONTEXTA pCryptAcquireContext = + GetProcAddress(hAdvAPI32, "CryptAcquireContextA"); + pCryptGenRandom = GetProcAddress(hAdvAPI32, "CryptGenRandom"); + /* Check the pointers to the CryptoAPI functions. These shouldn't fail + * but makes sure we won't have problems getting the context or getting + * random. If those aren't NULL then get the pCrypt context */ + return pCryptAcquireContext && pCryptGenRandom && + pCryptAcquireContext(&hCryptContext, NULL, NULL, PROV_RSA_FULL, + CRYPT_VERIFYCONTEXT) ? 1 : 0; + } + return 0; + } + MVMint32 MVM_getrandom (MVMThreadContext *tc, void *out, size_t size) { + /* Return 0 if the context doesn't exist and we are unable to create it */ + if (!hCryptContext && !win32_urandom_init()) + return 0; + return pCryptGenRandom(hCryptContext, (DWORD)size, (BYTE*)out) ? 1 : 0; + } +#else + MVMint32 MVM_getrandom (MVMThreadContext *tc, void *out, size_t size) { + return MVM_getrandom_urandom(tc, out, size); + } +#endif diff --git a/src/platform/random.h b/src/platform/random.h new file mode 100644 index 0000000000..95bbb5b1fa --- /dev/null +++ b/src/platform/random.h @@ -0,0 +1 @@ +MVMint32 MVM_getrandom (MVMThreadContext *tc, void *out, size_t size); diff --git a/src/strings/ops.c b/src/strings/ops.c index d827d5c4e5..57ec742268 100644 --- a/src/strings/ops.c +++ b/src/strings/ops.c @@ -2898,7 +2898,7 @@ void MVM_string_compute_hash_code(MVMThreadContext *tc, MVMString *s) { MVMuint32 graphs_remaining = MVM_string_graphs(tc, s); /* Initialize hash state. */ - MVMuint32 hashv = 0xfeedbeef; + MVMuint32 hashv = tc->instance->hashSecret; MVMuint32 _hj_i, _hj_j; _hj_i = _hj_j = 0x9e3779b9;