diff --git a/src/modules/math/RandomGenerator.cpp b/src/modules/math/RandomGenerator.cpp index fd27bdcc8..a40a61bd3 100644 --- a/src/modules/math/RandomGenerator.cpp +++ b/src/modules/math/RandomGenerator.cpp @@ -45,6 +45,15 @@ RandomGenerator::RandomGenerator() #endif } +void RandomGenerator::setState(RandomGenerator::State state) +{ + // 0 xor 0 is still 0, so Xorshift can't generate new numbers. + if (state.b64 == 0) + throw love::Exception("Invalid random state."); + + rng_state = state; +} + uint64 RandomGenerator::rand() { rng_state.b64 ^= (rng_state.b64 << 13); diff --git a/src/modules/math/RandomGenerator.h b/src/modules/math/RandomGenerator.h index ae0975c24..2016c3b4b 100644 --- a/src/modules/math/RandomGenerator.h +++ b/src/modules/math/RandomGenerator.h @@ -23,6 +23,7 @@ // LOVE #include "common/config.h" +#include "common/Exception.h" #include "common/math.h" #include "common/int.h" #include "common/Object.h" @@ -56,23 +57,24 @@ public: * Set pseudo-random state. * It's up to the implementation how to use this. **/ - inline void setState(State state) - { - rng_state = state; - } + void setState(State state); /** * Separately set the low and high parts of the pseudo-random state. **/ inline void setState(uint32 low, uint32 high) { + State newstate; + #ifdef LOVE_BIG_ENDIAN - rng_state.b32.a = high; - rng_state.b32.b = low; + newstate.b32.a = high; + newstate.b32.b = low; #else - rng_state.b32.b = high; - rng_state.b32.a = low; + newstate.b32.b = high; + newstate.b32.a = low; #endif + + setState(newstate); } inline State getState() const diff --git a/src/modules/math/wrap_Math.cpp b/src/modules/math/wrap_Math.cpp index 2dc520446..bdfb48c2c 100644 --- a/src/modules/math/wrap_Math.cpp +++ b/src/modules/math/wrap_Math.cpp @@ -34,7 +34,14 @@ namespace math int w_setRandomState(lua_State *L) { - Math::instance.setRandomState(luax_checkrandomstate(L, 1)); + try + { + Math::instance.setRandomState(luax_checkrandomstate(L, 1)); + } + catch (love::Exception &e) + { + return luaL_error(L, "%s", e.what()); + } return 0; } diff --git a/src/modules/math/wrap_RandomGenerator.cpp b/src/modules/math/wrap_RandomGenerator.cpp index 46674209f..c3f7f7576 100644 --- a/src/modules/math/wrap_RandomGenerator.cpp +++ b/src/modules/math/wrap_RandomGenerator.cpp @@ -21,20 +21,34 @@ #include "wrap_RandomGenerator.h" #include +#include namespace love { namespace math { +template +static T checkrandomstate_part(lua_State *L, int idx) +{ + double num = luaL_checknumber(L, idx); + double inf = std::numeric_limits::infinity(); + + // Disallow conversions from infinity and NaN. + if (num == inf || num == -inf || num != num) + luaL_argerror(L, idx, "invalid random state"); + + return (T) num; +} + RandomGenerator::State luax_checkrandomstate(lua_State *L, int idx) { RandomGenerator::State s; if (!lua_isnoneornil(L, idx + 1)) { - uint32 low = (uint32) luaL_checknumber(L, idx); - uint32 high = (uint32) luaL_checknumber(L, idx + 1); + uint32 low = checkrandomstate_part(L, idx); + uint32 high = checkrandomstate_part(L, idx + 1); #ifdef LOVE_BIG_ENDIAN s.b32.a = high; @@ -45,7 +59,7 @@ RandomGenerator::State luax_checkrandomstate(lua_State *L, int idx) #endif } else - s.b64 = (uint64) luaL_checknumber(L, idx); + s.b64 = checkrandomstate_part(L, idx); return s; } @@ -84,7 +98,14 @@ RandomGenerator *luax_checkrandomgenerator(lua_State *L, int idx) int w_RandomGenerator_setState(lua_State *L) { RandomGenerator *rng = luax_checkrandomgenerator(L, 1); - rng->setState(luax_checkrandomstate(L, 2)); + try + { + rng->setState(luax_checkrandomstate(L, 2)); + } + catch (love::Exception &e) + { + return luaL_error(L, "%s", e.what()); + } return 0; }