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_) |