diff options
| author | Benoit Germain <benoit.germain@ubisoft.com> | 2024-06-17 18:16:46 +0200 |
|---|---|---|
| committer | Benoit Germain <benoit.germain@ubisoft.com> | 2024-06-17 18:16:46 +0200 |
| commit | 54b47307ce4b2e21bc12c1602c77fecf55380452 (patch) | |
| tree | 9e807499e9bde6ba7193e98d5f611b2d66173490 /src | |
| parent | ca46cc7883b7b2749307a3e5a8196368fb52ba09 (diff) | |
| download | lanes-54b47307ce4b2e21bc12c1602c77fecf55380452.tar.gz lanes-54b47307ce4b2e21bc12c1602c77fecf55380452.tar.bz2 lanes-54b47307ce4b2e21bc12c1602c77fecf55380452.zip | |
Foolproofed config.allocator when it is a function
Diffstat (limited to 'src')
| -rw-r--r-- | src/lanes.lua | 8 | ||||
| -rw-r--r-- | src/state.cpp | 3 | ||||
| -rw-r--r-- | src/universe.cpp | 36 | ||||
| -rw-r--r-- | src/universe.h | 10 |
4 files changed, 37 insertions, 20 deletions
diff --git a/src/lanes.lua b/src/lanes.lua index 6342ca9..7c1f8df 100644 --- a/src/lanes.lua +++ b/src/lanes.lua | |||
| @@ -108,7 +108,7 @@ local default_params = | |||
| 108 | -- ################################################################################################# | 108 | -- ################################################################################################# |
| 109 | 109 | ||
| 110 | local boolean_param_checker = function(val_) | 110 | local boolean_param_checker = function(val_) |
| 111 | -- non-'boolean-false' should be 'boolean-true' or nil | 111 | -- non-'boolean-false|nil' should be 'boolean-true' |
| 112 | return (not val_) or (val_ == true) | 112 | return (not val_) or (val_ == true) |
| 113 | end | 113 | end |
| 114 | 114 | ||
| @@ -116,7 +116,7 @@ local param_checkers = | |||
| 116 | { | 116 | { |
| 117 | allocator = function(val_) | 117 | allocator = function(val_) |
| 118 | -- can be nil, "protected", or a function | 118 | -- can be nil, "protected", or a function |
| 119 | return val_ and (type(val_) == "function" or val_ == "protected") or true | 119 | return (val_ == nil) or (type(val_) == "function" or val_ == "protected") |
| 120 | end, | 120 | end, |
| 121 | internal_allocator = function(val_) | 121 | internal_allocator = function(val_) |
| 122 | -- can be "libc" or "allocator" | 122 | -- can be "libc" or "allocator" |
| @@ -128,11 +128,11 @@ local param_checkers = | |||
| 128 | end, | 128 | end, |
| 129 | nb_user_keepers = function(val_) | 129 | nb_user_keepers = function(val_) |
| 130 | -- nb_user_keepers should be a number in [0,100] (so that nobody tries to run OOM by specifying a huge amount) | 130 | -- nb_user_keepers should be a number in [0,100] (so that nobody tries to run OOM by specifying a huge amount) |
| 131 | return type(val_) == "number" and val_ >= 0 and val_ <= 100 | 131 | return (type(val_) == "number") and (val_ >= 0) and (val_ <= 100) |
| 132 | end, | 132 | end, |
| 133 | on_state_create = function(val_) | 133 | on_state_create = function(val_) |
| 134 | -- on_state_create may be nil or a function | 134 | -- on_state_create may be nil or a function |
| 135 | return val_ and type(val_) == "function" or true | 135 | return (val_ == nil) or (type(val_) == "function") |
| 136 | end, | 136 | end, |
| 137 | shutdown_mode = function(val_) | 137 | shutdown_mode = function(val_) |
| 138 | local valid_hooks = { soft = true, hard = true, call = true, ret = true, line = true, count = true } | 138 | local valid_hooks = { soft = true, hard = true, call = true, ret = true, line = true, count = true } |
diff --git a/src/state.cpp b/src/state.cpp index 3f6b3d7..ee0b199 100644 --- a/src/state.cpp +++ b/src/state.cpp | |||
| @@ -208,6 +208,9 @@ namespace state { | |||
| 208 | lua_pushcclosure(from, U->provideAllocator, 0); | 208 | lua_pushcclosure(from, U->provideAllocator, 0); |
| 209 | lua_call(from, 0, 1); | 209 | lua_call(from, 0, 1); |
| 210 | AllocatorDefinition* const _def{ luaG_tofulluserdata<AllocatorDefinition>(from, -1) }; | 210 | AllocatorDefinition* const _def{ luaG_tofulluserdata<AllocatorDefinition>(from, -1) }; |
| 211 | if (!_def || _def->version != AllocatorDefinition::kAllocatorVersion) { | ||
| 212 | raise_luaL_error(from, "Bad config.allocator function, must provide a valid AllocatorDefinition"); | ||
| 213 | } | ||
| 211 | lua_State* const _L{ lua_newstate(_def->allocF, _def->allocUD) }; | 214 | lua_State* const _L{ lua_newstate(_def->allocF, _def->allocUD) }; |
| 212 | lua_pop(from, 1); | 215 | lua_pop(from, 1); |
| 213 | return _L; | 216 | return _L; |
diff --git a/src/universe.cpp b/src/universe.cpp index c98e2c8..d24a784 100644 --- a/src/universe.cpp +++ b/src/universe.cpp | |||
| @@ -176,9 +176,23 @@ Universe::Universe() | |||
| 176 | // Do I need to disable this when compiling for LuaJIT to prevent issues? | 176 | // Do I need to disable this when compiling for LuaJIT to prevent issues? |
| 177 | void Universe::initializeAllocatorFunction(lua_State* const L_) | 177 | void Universe::initializeAllocatorFunction(lua_State* const L_) |
| 178 | { | 178 | { |
| 179 | // start by just grabbing whatever allocator was provided to lua_newstate | ||
| 180 | protectedAllocator.initFrom(L_); | ||
| 179 | STACK_CHECK_START_REL(L_, 1); // L_: settings | 181 | STACK_CHECK_START_REL(L_, 1); // L_: settings |
| 180 | if (luaG_getfield(L_, -1, "allocator") != LuaType::NIL) { // L_: settings allocator|nil|"protected" | 182 | switch (luaG_getfield(L_, -1, "allocator")) { // L_: settings allocator|nil|"protected" |
| 181 | // store C function pointer in an internal variable | 183 | case LuaType::NIL: |
| 184 | // nothing else to do | ||
| 185 | break; | ||
| 186 | |||
| 187 | case LuaType::STRING: | ||
| 188 | LUA_ASSERT(L_, luaG_tostring(L_, -1) == "protected"); | ||
| 189 | // set the original allocator to call from inside protection by the mutex | ||
| 190 | protectedAllocator.installIn(L_); | ||
| 191 | // before a state is created, this function will be called to obtain the allocator | ||
| 192 | provideAllocator = luaG_provide_protected_allocator; | ||
| 193 | break; | ||
| 194 | |||
| 195 | case LuaType::FUNCTION: | ||
| 182 | provideAllocator = lua_tocfunction(L_, -1); // L_: settings allocator | 196 | provideAllocator = lua_tocfunction(L_, -1); // L_: settings allocator |
| 183 | if (provideAllocator != nullptr) { | 197 | if (provideAllocator != nullptr) { |
| 184 | // make sure the function doesn't have upvalues | 198 | // make sure the function doesn't have upvalues |
| @@ -190,17 +204,13 @@ void Universe::initializeAllocatorFunction(lua_State* const L_) | |||
| 190 | // when we transfer the config table in newly created Lua states | 204 | // when we transfer the config table in newly created Lua states |
| 191 | lua_pushnil(L_); // L_: settings allocator nil | 205 | lua_pushnil(L_); // L_: settings allocator nil |
| 192 | lua_setfield(L_, -3, "allocator"); // L_: settings allocator | 206 | lua_setfield(L_, -3, "allocator"); // L_: settings allocator |
| 193 | } else if (luaG_type(L_, -1) == LuaType::STRING) { // should be "protected" | 207 | } else { |
| 194 | LUA_ASSERT(L_, strcmp(lua_tostring(L_, -1), "protected") == 0); | 208 | raise_luaL_error(L_, "Bad config.allocator, must be a C function"); |
| 195 | // set the original allocator to call from inside protection by the mutex | ||
| 196 | protectedAllocator.initFrom(L_); | ||
| 197 | protectedAllocator.installIn(L_); | ||
| 198 | // before a state is created, this function will be called to obtain the allocator | ||
| 199 | provideAllocator = luaG_provide_protected_allocator; | ||
| 200 | } | 209 | } |
| 201 | } else { | 210 | break; |
| 202 | // just grab whatever allocator was provided to lua_newstate | 211 | |
| 203 | protectedAllocator.initFrom(L_); | 212 | default: // should be filtered out in lanes.lua |
| 213 | raise_luaL_error(L_, "Bad config.allocator type %s", luaG_typename(L_, -1).data()); | ||
| 204 | } | 214 | } |
| 205 | lua_pop(L_, 1); // L_: settings | 215 | lua_pop(L_, 1); // L_: settings |
| 206 | STACK_CHECK(L_, 1); | 216 | STACK_CHECK(L_, 1); |
| @@ -208,7 +218,7 @@ void Universe::initializeAllocatorFunction(lua_State* const L_) | |||
| 208 | std::ignore = luaG_getfield(L_, -1, "internal_allocator"); // L_: settings "libc"|"allocator" | 218 | std::ignore = luaG_getfield(L_, -1, "internal_allocator"); // L_: settings "libc"|"allocator" |
| 209 | std::string_view const _allocator{ luaG_tostring(L_, -1) }; | 219 | std::string_view const _allocator{ luaG_tostring(L_, -1) }; |
| 210 | if (_allocator == "libc") { | 220 | if (_allocator == "libc") { |
| 211 | internalAllocator = AllocatorDefinition{ libc_lua_Alloc, nullptr }; | 221 | internalAllocator = AllocatorDefinition{ AllocatorDefinition::kAllocatorVersion, libc_lua_Alloc, nullptr }; |
| 212 | } else if (provideAllocator == luaG_provide_protected_allocator) { | 222 | } else if (provideAllocator == luaG_provide_protected_allocator) { |
| 213 | // user wants mutex protection on the state's allocator. Use protection for our own allocations too, just in case. | 223 | // user wants mutex protection on the state's allocator. Use protection for our own allocations too, just in case. |
| 214 | internalAllocator = protectedAllocator.makeDefinition(); | 224 | internalAllocator = protectedAllocator.makeDefinition(); |
diff --git a/src/universe.h b/src/universe.h index 47b86af..3742ddd 100644 --- a/src/universe.h +++ b/src/universe.h | |||
| @@ -18,6 +18,9 @@ class Lane; | |||
| 18 | class AllocatorDefinition | 18 | class AllocatorDefinition |
| 19 | { | 19 | { |
| 20 | public: | 20 | public: |
| 21 | // xxh64 of string "kAllocatorVersion_1" generated at https://www.pelock.com/products/hash-calculator | ||
| 22 | static constexpr uintptr_t kAllocatorVersion{ static_cast<uintptr_t>(0xCF9D321B0DFB5715ull) }; | ||
| 23 | uintptr_t version{ kAllocatorVersion }; | ||
| 21 | lua_Alloc allocF{ nullptr }; | 24 | lua_Alloc allocF{ nullptr }; |
| 22 | void* allocUD{ nullptr }; | 25 | void* allocUD{ nullptr }; |
| 23 | 26 | ||
| @@ -27,8 +30,9 @@ class AllocatorDefinition | |||
| 27 | // can't actually delete the operator because the compiler generates stack unwinding code that could call it in case of exception | 30 | // can't actually delete the operator because the compiler generates stack unwinding code that could call it in case of exception |
| 28 | static void operator delete([[maybe_unused]] void* p_, [[maybe_unused]] lua_State* L_) { LUA_ASSERT(L_, !"should never be called"); } | 31 | static void operator delete([[maybe_unused]] void* p_, [[maybe_unused]] lua_State* L_) { LUA_ASSERT(L_, !"should never be called"); } |
| 29 | 32 | ||
| 30 | AllocatorDefinition(lua_Alloc allocF_, void* allocUD_) noexcept | 33 | AllocatorDefinition(uintptr_t const version_, lua_Alloc const allocF_, void* const allocUD_) noexcept |
| 31 | : allocF{ allocF_ } | 34 | : version{ version_ } |
| 35 | , allocF{ allocF_ } | ||
| 32 | , allocUD{ allocUD_ } | 36 | , allocUD{ allocUD_ } |
| 33 | { | 37 | { |
| 34 | } | 38 | } |
| @@ -77,7 +81,7 @@ class ProtectedAllocator | |||
| 77 | 81 | ||
| 78 | AllocatorDefinition makeDefinition() | 82 | AllocatorDefinition makeDefinition() |
| 79 | { | 83 | { |
| 80 | return AllocatorDefinition{ protected_lua_Alloc, this }; | 84 | return AllocatorDefinition{ version, protected_lua_Alloc, this }; |
| 81 | } | 85 | } |
| 82 | 86 | ||
| 83 | void installIn(lua_State* L_) | 87 | void installIn(lua_State* L_) |
