add SYS_getrandom fallback for posix PAL (#431)

* add SYS_getrandom fallback for posix PAL

Signed-off-by: SchrodingerZhu <i@zhuyi.fan>

* address CR

Signed-off-by: SchrodingerZhu <i@zhuyi.fan>

* guard and comments

Signed-off-by: SchrodingerZhu <i@zhuyi.fan>

* more fallback behavior

Signed-off-by: SchrodingerZhu <i@zhuyi.fan>

* fix random device fd

Signed-off-by: SchrodingerZhu <i@zhuyi.fan>

* special handling for EAGAIN

Signed-off-by: SchrodingerZhu <i@zhuyi.fan>
This commit is contained in:
Schrodinger ZHU Yifan
2021-12-02 18:38:50 +08:00
committed by GitHub
parent 9329db102f
commit 71d5bb8756
3 changed files with 123 additions and 11 deletions

View File

@@ -89,7 +89,7 @@ CHECK_CXX_SOURCE_COMPILES("
# include <unistd.h>
#endif
#if __has_include(<sys/random.h>)
#include <sys/random.h>
# include <sys/random.h>
#endif
int main() {
int entropy = 0;
@@ -97,7 +97,6 @@ int main() {
return res;
}
" SNMALLOC_PLATFORM_HAS_GETENTROPY)
# Provide as function so other projects can reuse
# FIXME: This modifies some variables that may or may not be the ones that
# provide flags and so is broken by design. It should be removed once Verona
@@ -123,7 +122,7 @@ endfunction()
function(clangformat_targets)
# The clang-format tool is installed under a variety of different names. Try
# to find a sensible one. Only look for versions 9 explicitly - we don't
# know whether our clang-format file will work with newer versions of the
# know whether our clang-format file will work with newer versions of the
# tool. It does not work with older versions as AfterCaseLabel is not supported
# in earlier versions.
find_program(CLANG_FORMAT NAMES
@@ -196,6 +195,7 @@ add_as_define(USE_SNMALLOC_STATS)
add_as_define(SNMALLOC_QEMU_WORKAROUND)
add_as_define(SNMALLOC_CI_BUILD)
add_as_define(SNMALLOC_PLATFORM_HAS_GETENTROPY)
target_compile_definitions(snmalloc INTERFACE $<$<BOOL:CONST_QUALIFIED_MALLOC_USABLE_SIZE>:MALLOC_USABLE_SIZE_QUALIFIER=const>)
# In debug and CI builds, link the backtrace library so that we can get stack

View File

@@ -4,8 +4,10 @@
# include "../ds/bits.h"
# include "pal_posix.h"
# include <fcntl.h>
# include <string.h>
# include <sys/mman.h>
# include <syscall.h>
extern "C" int puts(const char* str);
@@ -18,12 +20,9 @@ namespace snmalloc
* Bitmap of PalFeatures flags indicating the optional features that this
* PAL supports.
*
* Linux does not support any features other than those in a generic POSIX
* platform. This field is declared explicitly to remind anyone who
* extends this PAL that they may need to extend the set of advertised
* features.
* We always make sure that linux has entropy support.
*/
static constexpr uint64_t pal_features = PALPOSIX::pal_features;
static constexpr uint64_t pal_features = PALPOSIX::pal_features | Entropy;
static constexpr size_t page_size =
Aal::aal_name == PowerPC ? 0x10000 : PALPOSIX::page_size;
@@ -85,6 +84,122 @@ namespace snmalloc
madvise(p, size, MADV_FREE);
}
}
static uint64_t get_entropy64()
{
// TODO: If the system call fails then the POSIX PAL calls libc
// functions that can require malloc, which may result in deadlock.
// SYS_getrandom API stablized since 3.17.
// This fallback implementation is to aid some environments
// where SYS_getrandom is provided in kernel but the libc
// is not providing getentropy interface.
union
{
uint64_t result;
char buffer[sizeof(uint64_t)];
};
ssize_t ret;
// give a try to SYS_getrandom
# ifdef SYS_getrandom
static std::atomic_bool syscall_not_working = false;
// Relaxed ordering should be fine here. This function will be called
// during early initialisation, which will examine the availability in a
// protected routine.
if (false == syscall_not_working.load(std::memory_order_relaxed))
{
auto current = std::begin(buffer);
auto target = std::end(buffer);
while (auto length = target - current)
{
// Reading data via syscall from system entropy pool.
// According to both MUSL and GLIBC implementation, getentropy uses
// /dev/urandom (blocking API).
//
// The third argument here indicates:
// 1. `GRND_RANDOM` bit is not set, so the source of entropy will be
// `urandom`.
// 2. `GRND_NONBLOCK` bit is set. Since we are reading from
// `urandom`, this means if the entropy pool is
// not initialised, we will get a EAGAIN.
ret = syscall(SYS_getrandom, current, length, GRND_NONBLOCK);
// check whether are interrupt by a signal
if (SNMALLOC_UNLIKELY(ret < 0))
{
if (SNMALLOC_UNLIKELY(errno == EAGAIN))
{
// the system is going through early initialisation: at this stage
// it is very likely that snmalloc is being used in some system
// programs and we do not want to block it.
return reinterpret_cast<uint64_t>(&result) ^
reinterpret_cast<uint64_t>(&error);
}
if (errno != EINTR)
{
break;
}
}
else
{
current += ret;
}
}
if (SNMALLOC_UNLIKELY(target != current))
{
// in this routine, the only possible situations should be ENOSYS
// or EPERM (forbidden by seccomp, for example).
SNMALLOC_ASSERT(errno == ENOSYS || errno == EPERM);
syscall_not_working.store(true, std::memory_order_relaxed);
}
else
{
return result;
}
}
# endif
// Syscall is not working.
// In this case, it is not a good idea to fallback to std::random_device:
// 1. it may want to use malloc to create a buffer, which causes
// reentrancy problem during initialisation routine.
// 2. some implementations also require libstdc++ to be linked since
// its APIs are not exception-free.
int flags = O_RDONLY;
# if defined(O_CLOEXEC)
flags |= O_CLOEXEC;
# endif
auto fd = open("/dev/urandom", flags, 0);
if (fd > 0)
{
auto current = std::begin(buffer);
auto target = std::end(buffer);
while (auto length = static_cast<size_t>(target - current))
{
ret = read(fd, current, length);
if (ret <= 0)
{
if (errno != EAGAIN && errno != EINTR)
{
break;
}
}
else
{
current += ret;
}
}
ret = close(fd);
SNMALLOC_ASSERT(0 == ret);
if (SNMALLOC_LIKELY(target == current))
{
return result;
}
}
error("Failed to get system randomness");
}
};
} // namespace snmalloc
#endif

View File

@@ -19,9 +19,6 @@
#if __has_include(<sys/random.h>)
# include <sys/random.h>
#endif
#if __has_include(<unistd.h>)
# include <unistd.h>
#endif
extern "C" int puts(const char* str);