Add fallback for random number generator

`getrandom()` is available since Linux 3.17 (2014/10/05) yet there are older devices that don't
meet this requirement.

Closes #22691.
PR #22723.
This commit is contained in:
Chocobo1 2025-05-18 15:37:17 +08:00 committed by Vladimir Golovnev (Glassez)
parent 9133b16431
commit f51ad39ad9
No known key found for this signature in database
GPG key ID: 52A2C7DEE2DFA6F7
4 changed files with 49 additions and 8 deletions

View file

@ -42,7 +42,7 @@
uint32_t Utils::Random::rand(const uint32_t min, const uint32_t max) uint32_t Utils::Random::rand(const uint32_t min, const uint32_t max)
{ {
static RandomLayer layer; static const RandomLayer layer;
// new distribution is cheap: https://stackoverflow.com/a/19036349 // new distribution is cheap: https://stackoverflow.com/a/19036349
std::uniform_int_distribution<uint32_t> uniform(min, max); std::uniform_int_distribution<uint32_t> uniform(min, max);

View file

@ -27,6 +27,7 @@
*/ */
#include <cerrno> #include <cerrno>
#include <cstdio>
#include <cstring> #include <cstring>
#include <limits> #include <limits>
@ -44,6 +45,27 @@ namespace
RandomLayer() RandomLayer()
{ {
if (::getrandom(nullptr, 0, 0) < 0)
{
if (errno == ENOSYS)
{
// underlying kernel does not implement this system call
// fallback to `urandom`
m_randDev = fopen("/dev/urandom", "rb");
if (!m_randDev)
qFatal("Failed to open /dev/urandom. Reason: \"%s\". Error code: %d.", std::strerror(errno), errno);
}
else
{
qFatal("getrandom() error. Reason: \"%s\". Error code: %d.", std::strerror(errno), errno);
}
}
}
~RandomLayer()
{
if (m_randDev)
fclose(m_randDev);
} }
static constexpr result_type min() static constexpr result_type min()
@ -56,7 +78,15 @@ namespace
return std::numeric_limits<result_type>::max(); return std::numeric_limits<result_type>::max();
} }
result_type operator()() result_type operator()() const
{
if (!m_randDev)
return getRandomViaAPI();
return getRandomViaFile();
}
private:
result_type getRandomViaAPI() const
{ {
const int RETRY_MAX = 3; const int RETRY_MAX = 3;
@ -68,10 +98,21 @@ namespace
return buf; return buf;
if (result < 0) if (result < 0)
qFatal("getrandom() error. Reason: %s. Error code: %d.", std::strerror(errno), errno); qFatal("getrandom() error. Reason: \"%s\". Error code: %d.", std::strerror(errno), errno);
} }
qFatal("getrandom() failed. Reason: too many retries."); qFatal("getrandom() failed. Reason: too many retries.");
} }
result_type getRandomViaFile() const
{
result_type buf = 0;
if (fread(&buf, sizeof(buf), 1, m_randDev) == 1)
return buf;
qFatal("Read /dev/urandom error. Reason: \"%s\". Error code: %d.", std::strerror(errno), errno);
}
FILE *m_randDev = nullptr;
}; };
} }

View file

@ -46,7 +46,7 @@ namespace
: m_randDev {fopen("/dev/urandom", "rb")} : m_randDev {fopen("/dev/urandom", "rb")}
{ {
if (!m_randDev) if (!m_randDev)
qFatal("Failed to open /dev/urandom. Reason: %s. Error code: %d.", std::strerror(errno), errno); qFatal("Failed to open /dev/urandom. Reason: \"%s\". Error code: %d.", std::strerror(errno), errno);
} }
~RandomLayer() ~RandomLayer()
@ -67,10 +67,10 @@ namespace
result_type operator()() const result_type operator()() const
{ {
result_type buf = 0; result_type buf = 0;
if (fread(&buf, sizeof(buf), 1, m_randDev) != 1) if (fread(&buf, sizeof(buf), 1, m_randDev) == 1)
qFatal("Read /dev/urandom error. Reason: %s. Error code: %d.", std::strerror(errno), errno); return buf;
return buf; qFatal("Read /dev/urandom error. Reason: \"%s\". Error code: %d.", std::strerror(errno), errno);
} }
private: private:

View file

@ -60,7 +60,7 @@ namespace
return std::numeric_limits<result_type>::max(); return std::numeric_limits<result_type>::max();
} }
result_type operator()() result_type operator()() const
{ {
result_type buf = 0; result_type buf = 0;
const bool result = m_processPrng(reinterpret_cast<PBYTE>(&buf), sizeof(buf)); const bool result = m_processPrng(reinterpret_cast<PBYTE>(&buf), sizeof(buf));