serenity/AK/Random.h
Timothy Flynn f7960ffbe3 AK: Prevent passing lengths greater than 256 to getentropy()
From the getentropy() man page, "The maximum permitted value for the
length argument is 256". Several of our tests were passing lengths of
several thousand bytes, causing getentropy() to fail with EIO, which we
were completely ignoring. This caused these tests to only test long
sequences of 0x00.

We now loop over the provided buffer to fill it 256 bytes at a time. If
getentropy() fails for any reason, we fall back to the default method of
filling it with one random byte at a time.
2023-04-03 15:53:49 +02:00

89 lines
2 KiB
C++

/*
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Platform.h>
#include <AK/StdLibExtras.h>
#include <AK/Types.h>
#include <stdlib.h>
#if defined(__unix__)
# include <unistd.h>
#endif
#if defined(AK_OS_MACOS)
# include <sys/random.h>
#endif
namespace AK {
inline void fill_with_random([[maybe_unused]] void* buffer, [[maybe_unused]] size_t length)
{
#if defined(AK_OS_SERENITY) || defined(AK_OS_ANDROID)
arc4random_buf(buffer, length);
#elif defined(OSS_FUZZ)
#else
auto fill_with_random_fallback = [&]() {
char* char_buffer = static_cast<char*>(buffer);
for (size_t i = 0; i < length; i++)
char_buffer[i] = rand();
};
# if defined(__unix__) or defined(AK_OS_MACOS)
// The maximum permitted value for the getentropy length argument.
static constexpr size_t getentropy_length_limit = 256;
auto iterations = length / getentropy_length_limit;
auto remainder = length % getentropy_length_limit;
auto address = reinterpret_cast<FlatPtr>(buffer);
for (size_t i = 0; i < iterations; ++i) {
if (getentropy(reinterpret_cast<void*>(address), getentropy_length_limit) != 0) {
fill_with_random_fallback();
return;
}
address += getentropy_length_limit;
}
if (remainder == 0 || getentropy(reinterpret_cast<void*>(address), remainder) == 0)
return;
# endif
fill_with_random_fallback();
#endif
}
template<typename T>
inline T get_random()
{
T t;
fill_with_random(&t, sizeof(T));
return t;
}
u32 get_random_uniform(u32 max_bounds);
template<typename Collection>
inline void shuffle(Collection& collection)
{
// Fisher-Yates shuffle
for (size_t i = collection.size() - 1; i >= 1; --i) {
size_t j = get_random_uniform(i + 1);
AK::swap(collection[i], collection[j]);
}
}
}
#if USING_AK_GLOBALLY
using AK::fill_with_random;
using AK::get_random;
using AK::get_random_uniform;
using AK::shuffle;
#endif