diff --git a/include/sol/stack_core.hpp b/include/sol/stack_core.hpp index c80c564a..e3471120 100644 --- a/include/sol/stack_core.hpp +++ b/include/sol/stack_core.hpp @@ -94,16 +94,35 @@ namespace sol { return reinterpret_cast(align(alignment, reinterpret_cast(ptr), space)); } - constexpr std::uintptr_t align_one(std::size_t alignment, std::size_t size, std::uintptr_t target_alignment) { + constexpr std::uintptr_t align_one(std::size_t alignment, std::size_t size, std::uintptr_t ptr) { std::size_t space = (std::numeric_limits::max)(); - return align(alignment, target_alignment, space) + size; + return align(alignment, ptr, space) + size; } template - constexpr std::size_t aligned_space_for(std::uintptr_t alignment) { - std::uintptr_t start = alignment; - (void)detail::swallow { int {}, (alignment = align_one(std::alignment_of_v, sizeof(Args), alignment), int {})... }; - return static_cast(alignment - start); + constexpr std::size_t aligned_space_for(std::uintptr_t ptr) { + std::uintptr_t end = ptr; + ((end = align_one(alignof(Args), sizeof(Args), end)), ...); + return static_cast(end - ptr); + } + + template + constexpr std::size_t aligned_space_for() { + static_assert(sizeof...(Args) > 0); + + constexpr std::size_t max_arg_alignment = (std::max)({ alignof(Args)... }); + if constexpr (max_arg_alignment <= alignof(std::max_align_t)) { + // If all types are `good enough`, simply calculate alignment in case of the worst allocator + std::size_t worst_required_size = 0; + for (std::size_t ptr = 0; ptr < max_arg_alignment; ptr++) { + worst_required_size = std::max(worst_required_size, aligned_space_for(ptr)); + } + return worst_required_size; + } + else { + // For over-aligned types let's assume that every Arg in Args starts at the worst aligned address + return (aligned_space_for(0x1) + ...); + } } inline void* align_usertype_pointer(void* ptr) { @@ -219,26 +238,17 @@ namespace sol { T** pointerpointer = static_cast(alloc_newuserdata(L, sizeof(T*))); return pointerpointer; } - constexpr std::size_t initial_size = aligned_space_for(0x0); - constexpr std::size_t misaligned_size = aligned_space_for(0x1); + constexpr std::size_t initial_size = aligned_space_for(); std::size_t allocated_size = initial_size; void* unadjusted = alloc_newuserdata(L, initial_size); void* adjusted = align(std::alignment_of::value, unadjusted, allocated_size); if (adjusted == nullptr) { + // trash allocator can burn in hell lua_pop(L, 1); - // what kind of absolute garbage trash allocator are we dealing with? - // whatever, add some padding in the case of MAXIMAL alignment waste... - allocated_size = misaligned_size; - unadjusted = alloc_newuserdata(L, allocated_size); - adjusted = align(std::alignment_of::value, unadjusted, allocated_size); - if (adjusted == nullptr) { - // trash allocator can burn in hell - lua_pop(L, 1); - // luaL_error(L, "if you are the one that wrote this allocator you should feel bad for doing a - // worse job than malloc/realloc and should go read some books, yeah?"); - luaL_error(L, "cannot properly align memory for '%s'", detail::demangle().data()); - } + // luaL_error(L, "if you are the one that wrote this allocator you should feel bad for doing a + // worse job than malloc/realloc and should go read some books, yeah?"); + luaL_error(L, "cannot properly align memory for '%s'", detail::demangle().data()); } return static_cast(adjusted); } @@ -316,44 +326,20 @@ namespace sol { return allocationtarget; } - /* the assumption is that `alloc_newuserdata` -- unless someone - passes a specific lua_Alloc that gives us bogus, un-aligned pointers - -- uses malloc, which tends to hand out more or less aligned pointers to memory - (most of the time, anyhow) - - but it's not guaranteed, so we have to do a post-adjustment check and increase padding - - we do this preliminarily with compile-time stuff, to see - if we strike lucky with the allocator and alignment values - - otherwise, we have to re-allocate the userdata and - over-allocate some space for additional padding because - compilers are optimized for aligned reads/writes - (and clang will barf UBsan errors on us for not being aligned) - */ - constexpr std::size_t initial_size = aligned_space_for(0x0); - constexpr std::size_t misaligned_size = aligned_space_for(0x1); + constexpr std::size_t initial_size = aligned_space_for(); void* pointer_adjusted; void* data_adjusted; bool result = attempt_alloc(L, std::alignment_of_v, sizeof(T*), std::alignment_of_v, initial_size, pointer_adjusted, data_adjusted); if (!result) { - // we're likely to get something that fails to perform the proper allocation a second time, - // so we use the suggested_new_size bump to help us out here - pointer_adjusted = nullptr; - data_adjusted = nullptr; - result = attempt_alloc( - L, std::alignment_of_v, sizeof(T*), std::alignment_of_v, misaligned_size, pointer_adjusted, data_adjusted); - if (!result) { - if (pointer_adjusted == nullptr) { - luaL_error(L, "aligned allocation of userdata block (pointer section) for '%s' failed", detail::demangle().c_str()); - } - else { - luaL_error(L, "aligned allocation of userdata block (data section) for '%s' failed", detail::demangle().c_str()); - } - return nullptr; + if (pointer_adjusted == nullptr) { + luaL_error(L, "aligned allocation of userdata block (pointer section) for '%s' failed", detail::demangle().c_str()); } + else { + luaL_error(L, "aligned allocation of userdata block (data section) for '%s' failed", detail::demangle().c_str()); + } + return nullptr; } T** pointerpointer = reinterpret_cast(pointer_adjusted); @@ -382,8 +368,7 @@ namespace sol { return mem; } - constexpr std::size_t initial_size = aligned_space_for(0x0); - constexpr std::size_t misaligned_size = aligned_space_for(0x1); + constexpr std::size_t initial_size = aligned_space_for(); void* pointer_adjusted; void* dx_adjusted; @@ -399,33 +384,16 @@ namespace sol { id_adjusted, data_adjusted); if (!result) { - // we're likely to get something that fails to perform the proper allocation a second time, - // so we use the suggested_new_size bump to help us out here - pointer_adjusted = nullptr; - dx_adjusted = nullptr; - id_adjusted = nullptr; - data_adjusted = nullptr; - result = attempt_alloc_unique(L, - std::alignment_of_v, - sizeof(T*), - std::alignment_of_v, - misaligned_size, - pointer_adjusted, - dx_adjusted, - id_adjusted, - data_adjusted); - if (!result) { - if (pointer_adjusted == nullptr) { - luaL_error(L, "aligned allocation of userdata block (pointer section) for '%s' failed", detail::demangle().c_str()); - } - else if (dx_adjusted == nullptr) { - luaL_error(L, "aligned allocation of userdata block (deleter section) for '%s' failed", detail::demangle().c_str()); - } - else { - luaL_error(L, "aligned allocation of userdata block (data section) for '%s' failed", detail::demangle().c_str()); - } - return nullptr; + if (pointer_adjusted == nullptr) { + luaL_error(L, "aligned allocation of userdata block (pointer section) for '%s' failed", detail::demangle().c_str()); } + else if (dx_adjusted == nullptr) { + luaL_error(L, "aligned allocation of userdata block (deleter section) for '%s' failed", detail::demangle().c_str()); + } + else { + luaL_error(L, "aligned allocation of userdata block (data section) for '%s' failed", detail::demangle().c_str()); + } + return nullptr; } pref = static_cast(pointer_adjusted); @@ -450,22 +418,14 @@ namespace sol { return pointer; } - constexpr std::size_t initial_size = aligned_space_for(0x0); - constexpr std::size_t misaligned_size = aligned_space_for(0x1); + constexpr std::size_t initial_size = aligned_space_for(); std::size_t allocated_size = initial_size; void* unadjusted = alloc_newuserdata(L, allocated_size); void* adjusted = align(std::alignment_of_v, unadjusted, allocated_size); if (adjusted == nullptr) { lua_pop(L, 1); - // try again, add extra space for alignment padding - allocated_size = misaligned_size; - unadjusted = alloc_newuserdata(L, allocated_size); - adjusted = align(std::alignment_of_v, unadjusted, allocated_size); - if (adjusted == nullptr) { - lua_pop(L, 1); - luaL_error(L, "cannot properly align memory for '%s'", detail::demangle().data()); - } + luaL_error(L, "cannot properly align memory for '%s'", detail::demangle().data()); } return static_cast(adjusted); } diff --git a/tests/regression_tests/simple/source/XXXX - incorrect alignment calculation.cpp b/tests/regression_tests/simple/source/XXXX - incorrect alignment calculation.cpp index 4f7fe580..3c8f72b1 100644 --- a/tests/regression_tests/simple/source/XXXX - incorrect alignment calculation.cpp +++ b/tests/regression_tests/simple/source/XXXX - incorrect alignment calculation.cpp @@ -6,6 +6,10 @@ inline namespace sol2_regression_test_XXXX { struct Test { std::uint64_t dummy; }; + + struct alignas(1024) Test2 { + char dummy[1024]; + }; } // namespace sol2_regression_test_XXXX unsigned int regression_XXXX() { @@ -20,6 +24,10 @@ unsigned int regression_XXXX() { /// Note: may not panic depending on alignment of local variable `alignment_shim` in sol::detail::aligned_space_for lua["test"] = Test {}; + // Test also unique and over-aligned userdata + lua["test"] = std::make_unique(); + lua["test"] = Test2 {}; + return 0; }