static uint64_t get_entropy64()

in src/pal/pal_linux.h [87:169]


    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.
      return dev_urandom();
    }