Repository: LadybirdBrowser/ladybird
File: AK/Random.cpp (C++)

Code before:

#if defined(AK_OS_LINUX)
#   include <sys/random.h>
#endif

#if defined(AK_OS_WINDOWS)
#    include <windows.h>
#    include <AK/NumericLimits.h>
#    include <AK/Windows.h>
#endif

#if defined(AK_OS_LINUX)
static inline int csprng_chunk(void * const buf, const size_t size)
{
    int ret;

    VERIFY(size <= 256U);
    do {
        ret = getrandom(buf, size, 0);
    } while (ret == -1 && (errno == EINTR || errno == EAGAIN));

    return ret;
}
#endif

static int csprng(void * const buf, size_t size)
{
#if defined(AK_OS_SERENITY) || defined(AK_OS_ANDROID) || defined(AK_OS_BSD_GENERIC) || defined(AK_OS_HAIKU) || AK_LIBC_GLIBC_PREREQ(2, 36)
    arc4random_buf(buf, size);
#elif defined(AK_OS_LINUX)
    size_t chunk_size = 256U;

    do {
        if (size < chunk_size) {
            chunk_size = size;
            VERIFY(chunk_size > (size_t) 0U);
        }

        // This likely isn't possible, as the only errors that aren't going to be handled by csprng_chunk
        // are EFAULT (outside the accessible address space), ENOSYS (kernel doesn't implement the syscall), 
        // or EINVAL (invalid flags).
        // 
        // We will address the possibility of the error nonetheless.
        if (csprng_chunk(buf, chunk_size) != 0) [[unlikely]] {
            return -1;
        }

        size -= chunk_size;
        buf += chunk_size;
    } while (size > (size_t) 0U);
#elif defined(AK_OS_WINDOWS)

Review comment (suggestion):
Use `ErrorOr<void>` instead of exposing OS-specific error codes as an `int`.

Code after:
#    include <sys/random.h>
#endif

#if defined(AK_OS_WINDOWS)
#    include <AK/Windows.h>
#endif

static inline ErrorOr<void> csprng(void* const buf, size_t size)
{
    // We shouldn't use OpenSSL's RAND_bytes function here, because we want to avoid adding dependencies to AK.
    // Therefore, we will use the best platform-specific CSPRNG.
#if defined(AK_OS_SERENITY) || defined(AK_OS_ANDROID) || defined(AK_OS_BSD_GENERIC) || defined(AK_OS_HAIKU) || AK_LIBC_GLIBC_PREREQ(2, 36)
    // This target also covers MacOS and iOS and they both seem to support arc4random_buf
    arc4random_buf(buf, size);
#elif defined(AK_OS_LINUX)
    unsigned char* out = (unsigned char*)buf;
    while (size > 0u) {
        // EINTR can be handled safely by just trying again. Others are fatal
        // See manual for more details
        int ret = getrandom(out, size, 0);
        if (ret == -1 && errno != EINTR) [[unlikely]]
            return Error::from_errno(errno);

        // If ret > 0 then ret indicates how much was copied
        if (ret > 0) [[likely]] {
            size -= ret;
            out += ret;
        }
    }
#elif defined(AK_OS_WINDOWS)
    // Documented to always return TRUE
    g_system.ProcessPrng((PBYTE)buf, size);
#else
    static_assert(false, "This build target doesn't have a valid CSPRNG interface specified in AK/Random.cpp.");
#endif
    return {};
}

namespace AK {

void fill_with_random(Bytes bytes)
{
    MUST(csprng(bytes.data(), bytes.size()));
}

u32 get_random_uniform(u32 max_bounds)
{
    // If we try to divide all 2**32 numbers into groups of "max_bounds" numbers, we may end up
    // with a group around 2**32-1 that is a bit too small. For this reason, the implementation
    // `arc4random() % max_bounds` would be insufficient. Here we compute the last number of the
    // last "full group". Note that if max_bounds is a divisor of UINT32_MAX,