From 1556a92a386909e35e5af0b855abc1e4d5af2630 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Mon, 9 Dec 2019 23:50:47 -0800 Subject: [PATCH] random: use TinyMT instead of XorShift --- .../libstratosphere/include/stratosphere.hpp | 1 - .../include/stratosphere/os.hpp | 1 + .../{rnd/rnd_api.hpp => os/os_random.hpp} | 13 +- .../include/stratosphere/util.hpp | 1 + .../include/stratosphere/util/util_tinymt.hpp | 99 ++++++++++ .../libstratosphere/source/map/map_api.cpp | 51 ++--- .../os/impl/os_random_impl.hpp} | 8 +- .../os/impl/os_random_impl.os.horizon.cpp | 34 ++++ .../libstratosphere/source/os/os_random.cpp | 61 ++++++ .../libstratosphere/source/rnd/rnd_api.cpp | 129 ------------ .../source/util/util_tinymt.cpp | 187 ++++++++++++++++++ .../loader/source/ldr_process_creation.cpp | 5 +- 12 files changed, 415 insertions(+), 175 deletions(-) rename libraries/libstratosphere/include/stratosphere/{rnd/rnd_api.hpp => os/os_random.hpp} (70%) create mode 100644 libraries/libstratosphere/include/stratosphere/util/util_tinymt.hpp rename libraries/libstratosphere/{include/stratosphere/rnd.hpp => source/os/impl/os_random_impl.hpp} (86%) create mode 100644 libraries/libstratosphere/source/os/impl/os_random_impl.os.horizon.cpp create mode 100644 libraries/libstratosphere/source/os/os_random.cpp delete mode 100644 libraries/libstratosphere/source/rnd/rnd_api.cpp create mode 100644 libraries/libstratosphere/source/util/util_tinymt.cpp diff --git a/libraries/libstratosphere/include/stratosphere.hpp b/libraries/libstratosphere/include/stratosphere.hpp index aae1f69cd..0217bb5f3 100644 --- a/libraries/libstratosphere/include/stratosphere.hpp +++ b/libraries/libstratosphere/include/stratosphere.hpp @@ -44,7 +44,6 @@ #include "stratosphere/patcher.hpp" #include "stratosphere/pm.hpp" #include "stratosphere/reg.hpp" -#include "stratosphere/rnd.hpp" #include "stratosphere/ro.hpp" #include "stratosphere/settings.hpp" #include "stratosphere/sf.hpp" diff --git a/libraries/libstratosphere/include/stratosphere/os.hpp b/libraries/libstratosphere/include/stratosphere/os.hpp index 21e54e43d..26d69627e 100644 --- a/libraries/libstratosphere/include/stratosphere/os.hpp +++ b/libraries/libstratosphere/include/stratosphere/os.hpp @@ -20,6 +20,7 @@ #include "os/os_memory_common.hpp" #include "os/os_managed_handle.hpp" #include "os/os_process_handle.hpp" +#include "os/os_random.hpp" #include "os/os_mutex.hpp" #include "os/os_condvar.hpp" #include "os/os_rw_lock.hpp" diff --git a/libraries/libstratosphere/include/stratosphere/rnd/rnd_api.hpp b/libraries/libstratosphere/include/stratosphere/os/os_random.hpp similarity index 70% rename from libraries/libstratosphere/include/stratosphere/rnd/rnd_api.hpp rename to libraries/libstratosphere/include/stratosphere/os/os_random.hpp index bd3e1ef9b..463b95955 100644 --- a/libraries/libstratosphere/include/stratosphere/rnd/rnd_api.hpp +++ b/libraries/libstratosphere/include/stratosphere/os/os_random.hpp @@ -15,13 +15,14 @@ */ #pragma once -#include +#include "os_common_types.hpp" -namespace ams::rnd { +namespace ams::os { - /* Random utilities. */ - void GenerateRandomBytes(void* out, size_t size); - u32 GenerateRandomU32(u32 max = std::numeric_limits::max()); - u64 GenerateRandomU64(u64 max = std::numeric_limits::max()); + void GenerateRandomBytes(void *dst, size_t size); + + /* Convenience API. */ + u32 GenerateRandomU32(u32 max = std::numeric_limits::max()); + u64 GenerateRandomU64(u64 max = std::numeric_limits::max()); } diff --git a/libraries/libstratosphere/include/stratosphere/util.hpp b/libraries/libstratosphere/include/stratosphere/util.hpp index 93e0633c5..d5886fb06 100644 --- a/libraries/libstratosphere/include/stratosphere/util.hpp +++ b/libraries/libstratosphere/include/stratosphere/util.hpp @@ -18,3 +18,4 @@ #include "util/util_compression.hpp" #include "util/util_ini.hpp" +#include "util/util_tinymt.hpp" diff --git a/libraries/libstratosphere/include/stratosphere/util/util_tinymt.hpp b/libraries/libstratosphere/include/stratosphere/util/util_tinymt.hpp new file mode 100644 index 000000000..a8d1c3135 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/util/util_tinymt.hpp @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include + +namespace ams::util { + + /* Implementation of TinyMT (mersenne twister RNG). */ + /* Like Nintendo, we will use the sample parameters. */ + class TinyMT { + public: + static constexpr size_t NumStateWords = 4; + + struct State { + u32 data[NumStateWords]; + }; + private: + static constexpr u32 ParamMat1 = 0x8F7011EE; + static constexpr u32 ParamMat2 = 0xFC78FF1F; + static constexpr u32 ParamTmat = 0x3793FDFF; + + static constexpr u32 ParamMult = 0x6C078965; + static constexpr u32 ParamPlus = 0x0019660D; + static constexpr u32 ParamXor = 0x5D588B65; + + static constexpr u32 TopBitmask = 0x7FFFFFFF; + + static constexpr int MinimumInitIterations = 8; + static constexpr int NumDiscardedInitOutputs = 8; + private: + State state; + private: + /* Internal API. */ + void FinalizeInitialization(); + + u32 GenerateRandomU24() { return (this->GenerateRandomU32() >> 8); } + + static void GenerateInitialValuePlus(TinyMT::State *state, int index, u32 value); + static void GenerateInitialValueXor(TinyMT::State *state, int index); + public: + /* Public API. */ + + /* Initialization. */ + void Initialize(u32 seed); + void Initialize(const u32 *seed, int seed_count); + + /* State management. */ + void GetState(TinyMT::State *out) const; + void SetState(const TinyMT::State *state); + + /* Random generation. */ + void GenerateRandomBytes(void *dst, size_t size); + u32 GenerateRandomU32(); + + inline u64 GenerateRandomU64() { + const u32 lo = this->GenerateRandomU32(); + const u32 hi = this->GenerateRandomU32(); + return (static_cast(hi) << 32) | static_cast(lo); + } + + inline float GenerateRandomF32() { + /* Floats have 24 bits of mantissa. */ + constexpr int MantissaBits = 24; + return GenerateRandomU24() * (1.0f / (1ul << MantissaBits)); + } + + inline double GenerateRandomF64() { + /* Doubles have 53 bits of mantissa. */ + /* The smart way to generate 53 bits of random would be to use 32 bits */ + /* from the first rnd32() call, and then 21 from the second. */ + /* Nintendo does not. They use (32 - 5) = 27 bits from the first rnd32() */ + /* call, and (32 - 6) bits from the second. We'll do what they do, but */ + /* There's not a clear reason why. */ + constexpr int MantissaBits = 53; + constexpr int Shift1st = (64 - MantissaBits) / 2; + constexpr int Shift2nd = (64 - MantissaBits) - Shift1st; + + const u32 first = (this->GenerateRandomU32() >> Shift1st); + const u32 second = (this->GenerateRandomU32() >> Shift2nd); + + return (1.0 * first * (1ul << (32 - Shift2nd)) + second) * (1.0 / (1ul << MantissaBits)); + } + }; + +} \ No newline at end of file diff --git a/libraries/libstratosphere/source/map/map_api.cpp b/libraries/libstratosphere/source/map/map_api.cpp index 1e8614d89..c8f82b8f9 100644 --- a/libraries/libstratosphere/source/map/map_api.cpp +++ b/libraries/libstratosphere/source/map/map_api.cpp @@ -43,9 +43,9 @@ namespace ams::map { } const uintptr_t mem_end = mem_info.addr + mem_info.size; - if (mem_info.type == MemType_Reserved || mem_end < cur_base || (mem_end >> 31)) { - return svc::ResultOutOfMemory(); - } + R_UNLESS(mem_info.type != MemType_Reserved, svc::ResultOutOfMemory()); + R_UNLESS(cur_base <= mem_end, svc::ResultOutOfMemory()); + R_UNLESS(mem_end <= static_cast(std::numeric_limits::max()), svc::ResultOutOfMemory()); cur_base = mem_end; } while (true); @@ -61,22 +61,16 @@ namespace ams::map { cur_base = address_space.aslr_base; cur_end = cur_base + size; - if (cur_end <= cur_base) { - return svc::ResultOutOfMemory(); - } + R_UNLESS(cur_base < cur_end, svc::ResultOutOfMemory()); while (true) { if (address_space.heap_size && (address_space.heap_base <= cur_end - 1 && cur_base <= address_space.heap_end - 1)) { /* If we overlap the heap region, go to the end of the heap region. */ - if (cur_base == address_space.heap_end) { - return svc::ResultOutOfMemory(); - } + R_UNLESS(cur_base != address_space.heap_end, svc::ResultOutOfMemory()); cur_base = address_space.heap_end; } else if (address_space.alias_size && (address_space.alias_base <= cur_end - 1 && cur_base <= address_space.alias_end - 1)) { /* If we overlap the alias region, go to the end of the alias region. */ - if (cur_base == address_space.alias_end) { - return svc::ResultOutOfMemory(); - } + R_UNLESS(cur_base != address_space.alias_end, svc::ResultOutOfMemory()); cur_base = address_space.alias_end; } else { R_ASSERT(svcQueryMemory(&mem_info, &page_info, cur_base)); @@ -84,18 +78,13 @@ namespace ams::map { *out_address = cur_base; return ResultSuccess(); } - if (mem_info.addr + mem_info.size <= cur_base) { - return svc::ResultOutOfMemory(); - } + R_UNLESS(cur_base < mem_info.addr + mem_info.size, svc::ResultOutOfMemory()); + cur_base = mem_info.addr + mem_info.size; - if (cur_base >= address_space.aslr_end) { - return svc::ResultOutOfMemory(); - } + R_UNLESS(cur_base < address_space.aslr_end, svc::ResultOutOfMemory()); } cur_end = cur_base + size; - if (cur_base + size <= cur_base) { - return svc::ResultOutOfMemory(); - } + R_UNLESS(cur_base < cur_base + size, svc::ResultOutOfMemory()); } } @@ -103,19 +92,15 @@ namespace ams::map { AddressSpaceInfo address_space; R_TRY(GetProcessAddressSpaceInfo(&address_space, process_handle)); - if (size > address_space.aslr_size) { - return ro::ResultOutOfAddressSpace(); - } + R_UNLESS(size <= address_space.aslr_size, ro::ResultOutOfAddressSpace()); uintptr_t try_address; for (unsigned int i = 0; i < LocateRetryCount; i++) { - try_address = address_space.aslr_base + (rnd::GenerateRandomU64(static_cast(address_space.aslr_size - size) >> 12) << 12); + try_address = address_space.aslr_base + (os::GenerateRandomU64(static_cast(address_space.aslr_size - size) / os::MemoryPageSize) * os::MemoryPageSize); MappedCodeMemory tmp_mcm(process_handle, try_address, base_address, size); R_TRY_CATCH(tmp_mcm.GetResult()) { - R_CATCH(svc::ResultInvalidCurrentMemoryState) { - continue; - } + R_CATCH(svc::ResultInvalidCurrentMemoryState) { continue; } } R_END_TRY_CATCH; if (!CanAddGuardRegionsInProcess(process_handle, try_address, size)) { @@ -134,14 +119,12 @@ namespace ams::map { AddressSpaceInfo address_space; R_TRY(GetProcessAddressSpaceInfo(&address_space, process_handle)); - if (size > address_space.aslr_size) { - return ro::ResultOutOfAddressSpace(); - } + R_UNLESS(size <= address_space.aslr_size, ro::ResultOutOfAddressSpace()); uintptr_t try_address; for (unsigned int i = 0; i < LocateRetryCount; i++) { while (true) { - try_address = address_space.aslr_base + (rnd::GenerateRandomU64(static_cast(address_space.aslr_size - size) >> 12) << 12); + try_address = address_space.aslr_base + (os::GenerateRandomU64(static_cast(address_space.aslr_size - size) / os::MemoryPageSize) * os::MemoryPageSize); if (address_space.heap_size && (address_space.heap_base <= try_address + size - 1 && try_address <= address_space.heap_end - 1)) { continue; } @@ -153,9 +136,7 @@ namespace ams::map { MappedCodeMemory tmp_mcm(process_handle, try_address, base_address, size); R_TRY_CATCH(tmp_mcm.GetResult()) { - R_CATCH(svc::ResultInvalidCurrentMemoryState) { - continue; - } + R_CATCH(svc::ResultInvalidCurrentMemoryState) { continue; } } R_END_TRY_CATCH; if (!CanAddGuardRegionsInProcess(process_handle, try_address, size)) { diff --git a/libraries/libstratosphere/include/stratosphere/rnd.hpp b/libraries/libstratosphere/source/os/impl/os_random_impl.hpp similarity index 86% rename from libraries/libstratosphere/include/stratosphere/rnd.hpp rename to libraries/libstratosphere/source/os/impl/os_random_impl.hpp index a11eba7b2..f06dfb76d 100644 --- a/libraries/libstratosphere/include/stratosphere/rnd.hpp +++ b/libraries/libstratosphere/source/os/impl/os_random_impl.hpp @@ -13,7 +13,11 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - #pragma once +#include -#include "rnd/rnd_api.hpp" +namespace ams::os::impl { + + void InitializeRandomImpl(util::TinyMT *mt); + +} diff --git a/libraries/libstratosphere/source/os/impl/os_random_impl.os.horizon.cpp b/libraries/libstratosphere/source/os/impl/os_random_impl.os.horizon.cpp new file mode 100644 index 000000000..42adc420e --- /dev/null +++ b/libraries/libstratosphere/source/os/impl/os_random_impl.os.horizon.cpp @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include + +namespace ams::os::impl { + + void InitializeRandomImpl(util::TinyMT *mt) { + /* Retrieve entropy from kernel. */ + u32 seed[4]; + static_assert(util::size(seed) == util::TinyMT::NumStateWords); + + /* Nintendo does not check the result of these invocations, but we will for safety. */ + /* Nintendo uses entropy values 0, 1 to seed the public TinyMT random, and values */ + /* 2, 3 to seed os::detail::RngManager's private TinyMT random. */ + R_ASSERT(svcGetInfo(reinterpret_cast(&seed[0]), InfoType_RandomEntropy, INVALID_HANDLE, 0)); + R_ASSERT(svcGetInfo(reinterpret_cast(&seed[2]), InfoType_RandomEntropy, INVALID_HANDLE, 1)); + + mt->Initialize(seed, util::size(seed)); + } + +} diff --git a/libraries/libstratosphere/source/os/os_random.cpp b/libraries/libstratosphere/source/os/os_random.cpp new file mode 100644 index 000000000..c367108f8 --- /dev/null +++ b/libraries/libstratosphere/source/os/os_random.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "impl/os_random_impl.hpp" + +namespace ams::os { + + namespace { + + util::TinyMT g_random; + os::Mutex g_random_mutex; + bool g_initialized_random; + + template + inline T GenerateRandomTImpl(T max) { + static_assert(std::is_integral::value && std::is_unsigned::value); + const T EffectiveMax = (std::numeric_limits::max() / max) * max; + T cur_rnd; + while (true) { + os::GenerateRandomBytes(&cur_rnd, sizeof(T)); + if (cur_rnd < EffectiveMax) { + return cur_rnd % max; + } + } + } + + } + + void GenerateRandomBytes(void *dst, size_t size) { + std::scoped_lock lk(g_random_mutex); + + if (!g_initialized_random) { + impl::InitializeRandomImpl(&g_random); + g_initialized_random = true; + } + + g_random.GenerateRandomBytes(dst, size); + } + + u32 GenerateRandomU32(u32 max) { + return GenerateRandomTImpl(max); + } + + u64 GenerateRandomU64(u64 max) { + return GenerateRandomTImpl(max); + } + +} diff --git a/libraries/libstratosphere/source/rnd/rnd_api.cpp b/libraries/libstratosphere/source/rnd/rnd_api.cpp deleted file mode 100644 index e0553c748..000000000 --- a/libraries/libstratosphere/source/rnd/rnd_api.cpp +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright (c) 2018-2019 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#include -#include - -namespace ams::rnd { - - namespace { - - /* Generator type. */ - /* Official HOS uses TinyMT. This is high effort. Let's just use XorShift. */ - /* https://en.wikipedia.org/wiki/Xorshift */ - class XorShiftGenerator { - public: - using ResultType = uint32_t; - using result_type = ResultType; - static constexpr ResultType (min)() { return std::numeric_limits::min(); } - static constexpr ResultType (max)() { return std::numeric_limits::max(); } - static constexpr size_t SeedSize = 4; - private: - ResultType random_state[SeedSize]; - public: - - explicit XorShiftGenerator() { - /* Seed using process entropy. */ - u64 val = 0; - for (size_t i = 0; i < SeedSize; i++) { - R_ASSERT(svcGetInfo(&val, InfoType_RandomEntropy, INVALID_HANDLE, i)); - this->random_state[i] = ResultType(val); - } - } - - explicit XorShiftGenerator(std::random_device &rd) { - for (size_t i = 0; i < SeedSize; i++) { - this->random_state[i] = ResultType(rd()); - } - } - - ResultType operator()() { - ResultType s, t = this->random_state[3]; - t ^= t << 11; - t ^= t >> 8; - this->random_state[3] = this->random_state[2]; this->random_state[2] = this->random_state[1]; this->random_state[1] = (s = this->random_state[0]); - t ^= s; - t ^= s >> 19; - this->random_state[0] = t; - return t; - } - - void discard(size_t n) { - for (size_t i = 0; i < n; i++) { - operator()(); - } - } - }; - - /* Generator global. */ - XorShiftGenerator g_rnd_generator; - - /* Templated helpers. */ - template - T GenerateRandom(T max = std::numeric_limits::max()) { - std::uniform_int_distribution rnd(std::numeric_limits::min(), max); - return rnd(g_rnd_generator); - } - - } - - void GenerateRandomBytes(void* _out, size_t size) { - uintptr_t out = reinterpret_cast(_out); - uintptr_t end = out + size; - - /* Force alignment. */ - if (out % sizeof(u16) && out < end) { - *reinterpret_cast(out) = GenerateRandom(); - out += sizeof(u8); - } - if (out % sizeof(u32) && out < end) { - *reinterpret_cast(out) = GenerateRandom(); - out += sizeof(u16); - } - if (out % sizeof(u64) && out < end) { - *reinterpret_cast(out) = GenerateRandom(); - out += sizeof(u32); - } - - /* Perform as many aligned writes as possible. */ - while (out + sizeof(u64) <= end) { - *reinterpret_cast(out) = GenerateRandom(); - out += sizeof(u64); - } - - /* Do remainder writes. */ - if (out + sizeof(u32) <= end) { - *reinterpret_cast(out) = GenerateRandom(); - out += sizeof(u32); - } - if (out + sizeof(u16) <= end) { - *reinterpret_cast(out) = GenerateRandom(); - out += sizeof(u16); - } - if (out + sizeof(u8) <= end) { - *reinterpret_cast(out) = GenerateRandom(); - out += sizeof(u8); - } - } - - u32 GenerateRandomU32(u32 max) { - return GenerateRandom(max); - } - - u64 GenerateRandomU64(u64 max) { - return GenerateRandom(max); - } - -} diff --git a/libraries/libstratosphere/source/util/util_tinymt.cpp b/libraries/libstratosphere/source/util/util_tinymt.cpp new file mode 100644 index 000000000..dbdb34be9 --- /dev/null +++ b/libraries/libstratosphere/source/util/util_tinymt.cpp @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +namespace ams::util { + + namespace { + + constexpr inline u32 XorByShifted27(u32 value) { + return value ^ (value >> 27); + } + + constexpr inline u32 XorByShifted30(u32 value) { + return value ^ (value >> 30); + } + + } + + void TinyMT::GenerateInitialValuePlus(TinyMT::State *state, int index, u32 value) { + u32 &state0 = state->data[(index + 0) % NumStateWords]; + u32 &state1 = state->data[(index + 1) % NumStateWords]; + u32 &state2 = state->data[(index + 2) % NumStateWords]; + u32 &state3 = state->data[(index + 3) % NumStateWords]; + + const u32 x = XorByShifted27(state0 ^ state1 ^ state3) * ParamPlus; + const u32 y = x + index + value; + + state0 = y; + state1 += x; + state2 += y; + } + + void TinyMT::GenerateInitialValueXor(TinyMT::State *state, int index) { + u32 &state0 = state->data[(index + 0) % NumStateWords]; + u32 &state1 = state->data[(index + 1) % NumStateWords]; + u32 &state2 = state->data[(index + 2) % NumStateWords]; + u32 &state3 = state->data[(index + 3) % NumStateWords]; + + const u32 x = XorByShifted27(state0 + state1 + state3) * ParamXor; + const u32 y = x - index; + + state0 = y; + state1 ^= x; + state2 ^= y; + } + + void TinyMT::Initialize(u32 seed) { + this->state.data[0] = seed; + this->state.data[1] = ParamMat1; + this->state.data[2] = ParamMat2; + this->state.data[3] = ParamTmat; + + for (int i = 1; i < MinimumInitIterations; i++) { + const u32 mixed = XorByShifted30(this->state.data[(i - 1) % NumStateWords]); + this->state.data[i % NumStateWords] ^= mixed * ParamMult + i; + } + + this->FinalizeInitialization(); + } + + void TinyMT::Initialize(const u32 *seed, int seed_count) { + this->state.data[0] = 0; + this->state.data[1] = ParamMat1; + this->state.data[2] = ParamMat2; + this->state.data[3] = ParamTmat; + + { + const int num_init_iterations = std::max(seed_count, MinimumInitIterations); + + GenerateInitialValuePlus(&this->state, 0, seed_count); + + for (int i = 0; i < num_init_iterations; i++) { + GenerateInitialValuePlus(&this->state, (i + 1) % NumStateWords, (i < seed_count) ? seed[i] : 0); + } + + for (int i = 0; i < static_cast(NumStateWords); i++) { + GenerateInitialValueXor(&this->state, (i + 1 + num_init_iterations) % NumStateWords); + } + } + + this->FinalizeInitialization(); + } + + void TinyMT::FinalizeInitialization() { + const u32 state0 = this->state.data[0] & TopBitmask; + const u32 state1 = this->state.data[1]; + const u32 state2 = this->state.data[2]; + const u32 state3 = this->state.data[3]; + + if (state0 == 0 && state1 == 0 && state2 == 0 && state3 == 0) { + this->state.data[0] = 'T'; + this->state.data[1] = 'I'; + this->state.data[2] = 'N'; + this->state.data[3] = 'Y'; + } + + for (int i = 0; i < NumDiscardedInitOutputs; i++) { + this->GenerateRandomU32(); + } + } + + + void TinyMT::GetState(TinyMT::State *out) const { + std::memcpy(out->data, this->state.data, sizeof(this->state)); + } + + void TinyMT::SetState(const TinyMT::State *state) { + std::memcpy(this->state.data, state->data, sizeof(this->state)); + } + + void TinyMT::GenerateRandomBytes(void *dst, size_t size) { + const uintptr_t start = reinterpret_cast(dst); + const uintptr_t end = start + size; + const uintptr_t aligned_start = util::AlignUp(start, 4); + const uintptr_t aligned_end = util::AlignDown(end, 4); + + /* Make sure we're aligned. */ + if (start < aligned_start) { + const u32 rnd = this->GenerateRandomU32(); + std::memcpy(dst, &rnd, aligned_start - start); + } + + /* Write as many aligned u32s as we can. */ + { + u32 * cur_dst = reinterpret_cast(aligned_start); + u32 * const end_dst = reinterpret_cast(aligned_end); + + while (cur_dst < end_dst) { + *(cur_dst++) = this->GenerateRandomU32(); + } + } + + /* Handle any leftover unaligned data. */ + if (aligned_end < end) { + const u32 rnd = this->GenerateRandomU32(); + std::memcpy(reinterpret_cast(aligned_end), &rnd, end - aligned_end); + } + } + + u32 TinyMT::GenerateRandomU32() { + /* Advance state. */ + const u32 x0 = (this->state.data[0] & TopBitmask) ^ this->state.data[1] ^ this->state.data[2]; + const u32 y0 = this->state.data[3]; + const u32 x1 = x0 ^ (x0 << 1); + const u32 y1 = y0 ^ (y0 >> 1) ^ x1; + + const u32 state0 = this->state.data[1]; + u32 state1 = this->state.data[2]; + u32 state2 = x1 ^ (y1 << 10); + const u32 state3 = y1; + + if ((y1 & 1) != 0) { + state1 ^= ParamMat1; + state2 ^= ParamMat2; + } + + this->state.data[0] = state0; + this->state.data[1] = state1; + this->state.data[2] = state2; + this->state.data[3] = state3; + + /* Temper. */ + const u32 t1 = state0 + (state2 >> 8); + u32 t0 = state3 ^ t1; + + if ((t1 & 1) != 0) { + t0 ^= ParamTmat; + } + + return t0; + } + +} \ No newline at end of file diff --git a/stratosphere/loader/source/ldr_process_creation.cpp b/stratosphere/loader/source/ldr_process_creation.cpp index 1534280ec..93f4abf72 100644 --- a/stratosphere/loader/source/ldr_process_creation.cpp +++ b/stratosphere/loader/source/ldr_process_creation.cpp @@ -499,9 +499,10 @@ namespace ams::ldr { /* Set Create Process output. */ uintptr_t aslr_slide = 0; - uintptr_t unused_size = (aslr_size - total_size); + uintptr_t free_size = (aslr_size - total_size); if (out_cpi->flags & svc::CreateProcessFlag_EnableAslr) { - aslr_slide = ams::rnd::GenerateRandomU64(unused_size / os::MemoryBlockUnitSize) * os::MemoryBlockUnitSize; + /* Nintendo uses MT19937 (not os::GenerateRandomBytes), but we'll just use TinyMT for now. */ + aslr_slide = os::GenerateRandomU64(free_size / os::MemoryBlockUnitSize) * os::MemoryBlockUnitSize; } /* Set out. */