Index: lib/sanitizer_common/sanitizer_common.h =================================================================== --- lib/sanitizer_common/sanitizer_common.h +++ lib/sanitizer_common/sanitizer_common.h @@ -857,8 +857,8 @@ void CheckNoDeepBind(const char *filename, int flag); // Returns the requested amount of random data (up to 256 bytes) that can then -// be used to seed a PRNG. -bool GetRandom(void *buffer, uptr length); +// be used to seed a PRNG. Defaults to blocking like the underlying syscall. +bool GetRandom(void *buffer, uptr length, bool blocking = true); } // namespace __sanitizer Index: lib/sanitizer_common/sanitizer_linux.cc =================================================================== --- lib/sanitizer_common/sanitizer_linux.cc +++ lib/sanitizer_common/sanitizer_linux.cc @@ -125,6 +125,15 @@ } #endif +#if SANITIZER_LINUX && defined(__NR_getrandom) +# if !defined(GRND_NONBLOCK) +# define GRND_NONBLOCK 1 +# endif +# define SANITIZER_USE_GETRANDOM 1 +#else +# define SANITIZER_USE_GETRANDOM 0 +#endif // SANITIZER_LINUX && defined(__NR_getrandom) + namespace __sanitizer { #if SANITIZER_LINUX && defined(__x86_64__) @@ -1676,25 +1685,37 @@ return 0; } -bool GetRandom(void *buffer, uptr length) { +#if SANITIZER_USE_GETRANDOM +uptr internal_getrandom(void *buffer, uptr length, bool blocking) { + return internal_syscall(SYSCALL(getrandom), buffer, length, + blocking ? 0 : GRND_NONBLOCK); +} +#else +uptr internal_getrandom(void *buffer, uptr length, bool blocking) { + return static_cast(-ENOSYS); +} +#endif // SANITIZER_USE_GETRANDOM + +bool GetRandom(void *buffer, uptr length, bool blocking) { if (!buffer || !length || length > 256) return false; -#if defined(__NR_getrandom) - static atomic_uint8_t skip_getrandom_syscall; - if (!atomic_load_relaxed(&skip_getrandom_syscall)) { - // Up to 256 bytes, getrandom will not be interrupted. - uptr res = internal_syscall(SYSCALL(getrandom), buffer, length, 0); - int rverrno = 0; - if (internal_iserror(res, &rverrno) && rverrno == ENOSYS) - atomic_store_relaxed(&skip_getrandom_syscall, 1); - else if (res == length) - return true; + if (SANITIZER_USE_GETRANDOM) { + static atomic_uint8_t skip_getrandom_syscall; + if (!atomic_load_relaxed(&skip_getrandom_syscall)) { + // Up to 256 bytes, getrandom will not be interrupted. + uptr res = internal_getrandom(buffer, length, blocking); + int rverrno = 0; + if (internal_iserror(res, &rverrno) && rverrno == ENOSYS) + atomic_store_relaxed(&skip_getrandom_syscall, 1); + else if (res == length) + return true; + } } -#endif + // Up to 256 bytes, a read off /dev/urandom will not be interrupted. + // blocking is moot here, O_NONBLOCK has no effect when opening /dev/urandom. uptr fd = internal_open("/dev/urandom", O_RDONLY); if (internal_iserror(fd)) return false; - // internal_read deals with EINTR. uptr res = internal_read(fd, buffer, length); if (internal_iserror(res)) return false; Index: lib/sanitizer_common/sanitizer_mac.cc =================================================================== --- lib/sanitizer_common/sanitizer_mac.cc +++ lib/sanitizer_common/sanitizer_mac.cc @@ -992,7 +992,7 @@ } // FIXME: implement on this platform. -bool GetRandom(void *buffer, uptr length) { +bool GetRandom(void *buffer, uptr length, bool blocking) { UNIMPLEMENTED(); } Index: lib/sanitizer_common/sanitizer_win.cc =================================================================== --- lib/sanitizer_common/sanitizer_win.cc +++ lib/sanitizer_common/sanitizer_win.cc @@ -1022,7 +1022,7 @@ } // FIXME: implement on this platform. -bool GetRandom(void *buffer, uptr length) { +bool GetRandom(void *buffer, uptr length, bool blocking) { UNIMPLEMENTED(); } Index: lib/sanitizer_common/tests/sanitizer_common_test.cc =================================================================== --- lib/sanitizer_common/tests/sanitizer_common_test.cc +++ lib/sanitizer_common/tests/sanitizer_common_test.cc @@ -304,15 +304,17 @@ #if SANITIZER_LINUX TEST(SanitizerCommon, GetRandom) { u8 buffer_1[32], buffer_2[32]; - EXPECT_FALSE(GetRandom(nullptr, 32)); - EXPECT_FALSE(GetRandom(buffer_1, 0)); - EXPECT_FALSE(GetRandom(buffer_1, 512)); - EXPECT_EQ(ARRAY_SIZE(buffer_1), ARRAY_SIZE(buffer_2)); - for (uptr size = 4; size <= ARRAY_SIZE(buffer_1); size += 4) { - for (uptr i = 0; i < 100; i++) { - EXPECT_TRUE(GetRandom(buffer_1, size)); - EXPECT_TRUE(GetRandom(buffer_2, size)); - EXPECT_NE(internal_memcmp(buffer_1, buffer_2, size), 0); + for (bool blocking : { false, true }) { + EXPECT_FALSE(GetRandom(nullptr, 32, blocking)); + EXPECT_FALSE(GetRandom(buffer_1, 0, blocking)); + EXPECT_FALSE(GetRandom(buffer_1, 512, blocking)); + EXPECT_EQ(ARRAY_SIZE(buffer_1), ARRAY_SIZE(buffer_2)); + for (uptr size = 4; size <= ARRAY_SIZE(buffer_1); size += 4) { + for (uptr i = 0; i < 100; i++) { + EXPECT_TRUE(GetRandom(buffer_1, size, blocking)); + EXPECT_TRUE(GetRandom(buffer_2, size, blocking)); + EXPECT_NE(internal_memcmp(buffer_1, buffer_2, size), 0); + } } } } Index: lib/scudo/scudo_utils.h =================================================================== --- lib/scudo/scudo_utils.h +++ lib/scudo/scudo_utils.h @@ -44,11 +44,14 @@ struct XoRoShiRo128Plus { public: void init() { - if (UNLIKELY(!GetRandom(reinterpret_cast(State), sizeof(State)))) { - // Early processes (eg: init) do not have /dev/urandom yet, but we still - // have to provide them with some degree of entropy. Not having a secure - // seed is not as problematic for them, as they are less likely to be - // the target of heap based vulnerabilities exploitation attempts. + if (UNLIKELY(!GetRandom(reinterpret_cast(State), sizeof(State), + /*blocking=*/false))) { + // On some platforms, early processes like `init` do not have an + // initialized random pool (getrandom blocks and /dev/urandom doesn't + // exist yet), but we still have to provide them with some degree of + // entropy. Not having a secure seed is not as problematic for them, as + // they are less likely to be the target of heap based vulnerabilities + // exploitation attempts. State[0] = NanoTime(); State[1] = 0; }