-
Notifications
You must be signed in to change notification settings - Fork 201
Tweak ASAN compile & fix a couple of bugs #2663
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
- Remove restriction to DEBUG builds only - ASAN works well with optimized builds - Add -fPIC for shared library compatibility with ASAN - Add -fno-omit-frame-pointer for better stack traces in error reports - Add -fno-optimize-sibling-calls to preserve full call chains - Set -O2 optimization level for better error detection than -O3 - Add explanatory comments for each flag 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Replace std::initializer_list with std::array in wrapModes field.
std::initializer_list is just a view into a temporary array, so storing
it as a member variable creates a dangling pointer once the temporary
is destroyed.
ASAN error:
==559461==ERROR: AddressSanitizer: stack-use-after-scope on address 0x74d3039d3460 at pc 0x55c80ddb562f bp 0x7fffc14493c0 sp 0x7fffc14493b0
READ of size 4 at 0x74d3039d3460 thread T0 (recoil-main)
#0 0x55c80ddb562e in GL::Impl::InitTexture(GL::TextureCreationParams const&, unsigned int, int) /build/src/rts/Rendering/Textures/Texture.cpp:33
beyond-all-reason#1 0x55c80ddb721c in GL::Texture2D::Texture2D(int, int, unsigned int, GL::TextureCreationParams const&, bool) /build/src/rts/Rendering/Textures/Texture.cpp:133
beyond-all-reason#2 0x55c80daf951c in CHeightTexture::CHeightTexture() /build/src/rts/Rendering/Map/InfoTexture/Modern/Height.cpp:49
beyond-all-reason#3 0x55c80db02149 in CInfoTextureHandler::CInfoTextureHandler() /build/src/rts/Rendering/Map/InfoTexture/Modern/InfoTextureHandler.cpp:26
beyond-all-reason#4 0x55c80daec745 in std::__detail::_MakeUniq<CInfoTextureHandler>::__single_object std::make_unique<CInfoTextureHandler>() /usr/include/c++/13/bits/unique_ptr.h:1070
beyond-all-reason#5 0x55c80daec745 in IInfoTextureHandler::Create() /build/src/rts/Rendering/Map/InfoTexture/IInfoTextureHandler.cpp:25
beyond-all-reason#6 0x55c80df3b1d1 in CWorldDrawer::InitPost() const /build/src/rts/Rendering/WorldDrawer.cpp:111
beyond-all-reason#7 0x55c8107955f2 in CGame::PostLoadRendering() /build/src/rts/Game/Game.cpp:756
beyond-all-reason#8 0x55c8107955f2 in CGame::Load(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) /build/src/rts/Game/Game.cpp:411
beyond-all-reason#9 0x55c81084498e in CLoadScreen::Init() /build/src/rts/Game/LoadScreen.cpp:146
beyond-all-reason#10 0x55c810845a80 in CLoadScreen::CreateInstance(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&&, ILoadSaveHandler*) /build/src/rts/Game/LoadScreen.cpp:215
beyond-all-reason#11 0x55c810845a80 in CLoadScreen::CreateDeleteInstance(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&&, ILoadSaveHandler*) /build/src/rts/Game/LoadScreen.cpp:200
beyond-all-reason#12 0x55c8108758d6 in CPreGame::UpdateClientNet() /build/src/rts/Game/PreGame.cpp:470
beyond-all-reason#13 0x55c810876b6f in CPreGame::Update() /build/src/rts/Game/PreGame.cpp:241
beyond-all-reason#14 0x55c80e883938 in SpringApp::Update() /build/src/rts/System/SpringApp.cpp:886
beyond-all-reason#15 0x55c80e890ffb in SpringApp::Run() /build/src/rts/System/SpringApp.cpp:927
beyond-all-reason#16 0x55c80e7fb939 in Run(int, char**) /build/src/rts/System/Main.cpp:51
beyond-all-reason#17 0x55c80cd63653 in main /build/src/rts/System/Main.cpp:104
beyond-all-reason#18 0x74d30642a1c9 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
beyond-all-reason#19 0x74d30642a28a in __libc_start_main_impl ../csu/libc-start.c:360
beyond-all-reason#20 0x55c80ce28fc9 in _start (/home/gajop/projects/spring-projects/spring-bar/build-linux/install/spring+0x931fc9) (BuildId: 4378a67e1e8529ce2acbdc4e3f13182a8665a60b)
Address 0x74d3039d3460 is located in stack of thread T0 (recoil-main) at offset 1120 in frame
#0 0x55c80daf83cf in CHeightTexture::CHeightTexture() /build/src/rts/Rendering/Map/InfoTexture/Modern/Height.cpp:23
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implement deferred deletion pattern for SolLuaEventListener to prevent
use-after-free when OnDetach is called during ProcessEvent execution.
The issue occurred when a Lua event handler called SetInnerRML, which
destroyed elements and triggered OnDetach callbacks. OnDetach immediately
deleted the listener with "delete this", but ProcessEvent was still on
the call stack trying to invoke the Lua callback.
The fix defers deletion by:
1. Setting m_detached flag in OnDetach instead of immediate deletion
2. Checking the flag at the start of ProcessEvent (handles queued events)
3. Checking the flag at the end of ProcessEvent (handles mid-execution detachment)
This ensures the listener object is only deleted when it's safe to do so,
after all its methods have returned.
ASAN Error:
==704643==ERROR: AddressSanitizer: heap-use-after-free on address 0x5060002a98d8 at pc 0x618574f3991e bp 0x7ffe2f43de80 sp 0x7ffe2f43de70
READ of size 8 at 0x5060002a98d8 thread T0 (recoil-main)
#0 in sol::basic_reference<false>::lua_state() const /build/src/rts/lib/sol2/sol.hpp:10244
beyond-all-reason#1 in sol::protected_function_result sol::basic_protected_function<>::invoke<false>() /build/src/rts/lib/sol2/sol.hpp:20676
beyond-all-reason#2 in decltype(auto) sol::basic_protected_function<>::call<>(Rml::Event&, Rml::Element*&, Rml::SolLua::SolLuaDocument*&) /build/src/rts/lib/sol2/sol.hpp:20569
beyond-all-reason#3 in Rml::SolLua::SolLuaEventListener::ProcessEvent(Rml::Event&) /build/src/rts/Rml/SolLua/plugin/SolLuaEventListener.cpp:144
beyond-all-reason#4 in Rml::EventDispatcher::DispatchEvent() /build/src/rts/lib/RmlUi/Source/Core/EventDispatcher.cpp:194
beyond-all-reason#5 in Rml::Element::DispatchEvent() /build/src/rts/lib/RmlUi/Source/Core/Element.cpp:1201
beyond-all-reason#6 in Rml::Context::ProcessMouseButtonUp(int, int) /build/src/rts/lib/RmlUi/Source/Core/Context.cpp:738
beyond-all-reason#7-53 [Mouse event handling chain up to main]
0x5060002a98d8 is located 24 bytes inside of 56-byte region
freed by thread T0 (recoil-main) here:
#0 operator delete(void*, unsigned long)
beyond-all-reason#1 in Rml::SolLua::SolLuaEventListener::~SolLuaEventListener() /build/src/rts/Rml/SolLua/plugin/SolLuaEventListener.h:47
beyond-all-reason#2 in Rml::SolLua::SolLuaEventListener::OnDetach(Rml::Element*) /build/src/rts/Rml/SolLua/plugin/SolLuaEventListener.cpp:118
beyond-all-reason#3 in Rml::EventDispatcher::~EventDispatcher() /build/src/rts/lib/RmlUi/Source/Core/EventDispatcher.cpp:65
beyond-all-reason#4 in Rml::ElementMeta::~ElementMeta() /build/src/rts/lib/RmlUi/Source/Core/Element.cpp:94
beyond-all-reason#5 in Rml::Element::~Element() /build/src/rts/lib/RmlUi/Source/Core/Element.cpp:151
beyond-all-reason#6-14 [Element destruction chain]
beyond-all-reason#15 in Rml::Element::SetInnerRML() /build/src/rts/lib/RmlUi/Source/Core/Element.cpp:1100
beyond-all-reason#16-45 [Lua/Sol2 call stack from event handler calling SetInnerRML]
beyond-all-reason#46 in Rml::SolLua::SolLuaEventListener::ProcessEvent(Rml::Event&) /build/src/rts/Rml/SolLua/plugin/SolLuaEventListener.cpp:144
beyond-all-reason#47 in Rml::EventDispatcher::DispatchEvent() /build/src/rts/lib/RmlUi/Source/Core/EventDispatcher.cpp:194
beyond-all-reason#48-53 [Outer event handling that triggered the inner event]
previously allocated by thread T0 (recoil-main) here:
#0 operator new(unsigned long)
beyond-all-reason#1 in Rml::SolLua::functions::addEventListener() /build/src/rts/Rml/SolLua/bind/Element.cpp:47
beyond-all-reason#2-53 [Lua binding and event registration chain]
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Root cause: Context::Render was exposed to Lua without proper frame
management (BeginFrame/PresentFrame). When called from Lua for
render-to-texture workflows, PushLayer/PopLayer would execute outside
the frame lifecycle, causing the layer stack to underflow and access
freed memory.
Solution: Wrap the Lua binding for Context::Render with a lambda that
calls BeginFrame before and PresentFrame after rendering, ensuring
proper layer stack management for all callers including RTT use cases.
ASAN error:
==37278==ERROR: AddressSanitizer: heap-use-after-free on address 0x503000460eec at pc 0x5c05ab21d77a bp 0x7ffcce4f73e0 sp 0x7ffcce4f73d0
READ of size 4 at 0x503000460eec thread T0 (recoil-main)
#0 0x5c05ab21d779 in RenderInterface_GL3_Recoil::PopLayer() /build/src/rts/Rml/Backends/RmlUi_Renderer_GL3_Recoil.cpp:1727
beyond-all-reason#1 0x5c05ad40d908 in Rml::RenderManager::PopLayer() /build/src/rts/lib/RmlUi/Source/Core/RenderManager.cpp:326
beyond-all-reason#2 0x5c05ad2fac86 in Rml::ElementEffects::RenderEffects(Rml::RenderStage) /build/src/rts/lib/RmlUi/Source/Core/ElementEffects.cpp:336
beyond-all-reason#3 0x5c05ad2a037a in Rml::Element::Render() /build/src/rts/lib/RmlUi/Source/Core/Element.cpp:255
beyond-all-reason#4 0x5c05ad2a02eb in Rml::Element::Render() /build/src/rts/lib/RmlUi/Source/Core/Element.cpp:253
beyond-all-reason#5 0x5c05ad2a02eb in Rml::Element::Render() /build/src/rts/lib/RmlUi/Source/Core/Element.cpp:253
beyond-all-reason#6 0x5c05ad1e000a in Rml::Context::Render() /build/src/rts/lib/RmlUi/Source/Core/Context.cpp:221
beyond-all-reason#7 0x5c05ab3a434b in bool sol::member_function_wrapper<bool (Rml::Context::*)(), bool, Rml::Context>::call<bool (Rml::Context::*&)()>(bool (Rml::Context::*&)(), Rml::Context&) /build/src/rts/lib/sol2/sol.hpp:17338
beyond-all-reason#8 0x5c05ab3a434b in decltype(auto) sol::member_function_wrapper<bool (Rml::Context::*)(), bool, Rml::Context>::caller::operator()<bool (Rml::Context::*&)()>(bool (Rml::Context::*&)(), Rml::Context&) const /build/src/rts/lib/sol2/sol.hpp:17344
beyond-all-reason#9 0x5c05ab3a434b in eval<true, sol::argument_handler<sol::types<bool> >&, sol::member_function_wrapper<bool (Rml::Context::*)(), bool, Rml::Context>::caller, bool (Rml::Context::*&)(), Rml::Context&> /build/src/rts/lib/sol2/sol.hpp:16078
beyond-all-reason#10 0x5c05ab3a434b in decltype(auto) sol::stack::stack_detail::call<true, , bool, , sol::member_function_wrapper<bool (Rml::Context::*)(), bool, Rml::Context>::caller, bool (Rml::Context::*&)(), Rml::Context&>(sol::types<bool>, sol::types<>, std::integer_sequence<unsigned long>, lua_State*, int, sol::member_function_wrapper<bool (Rml::Context::*)(), bool, Rml::Context>::caller&&, bool (Rml::Context::*&)(), Rml::Context&) /build/src/rts/lib/sol2/sol.hpp:16131
beyond-all-reason#11 0x5c05ab3a434b in decltype(auto) sol::stack::call<true, bool, , sol::member_function_wrapper<bool (Rml::Context::*)(), bool, Rml::Context>::caller, bool (Rml::Context::*&)(), Rml::Context&>(sol::types<bool>, sol::types<>, lua_State*, int, sol::member_function_wrapper<bool (Rml::Context::*)(), bool, Rml::Context>::caller&&, bool (Rml::Context::*&)(), Rml::Context&) /build/src/rts/lib/sol2/sol.hpp:16150
beyond-all-reason#12 0x5c05ab3a434b in int sol::stack::call_into_lua<true, true, bool, , , sol::member_function_wrapper<bool (Rml::Context::*)(), bool, Rml::Context>::caller, bool (Rml::Context::*&)(), Rml::Context&>(sol::types<bool>, sol::types<>, lua_State*, int, sol::member_function_wrapper<bool (Rml::Context::*)(), bool, Rml::Context>::caller&&, bool (Rml::Context::*&)(), Rml::Context&) /build/src/rts/lib/sol2/sol.hpp:16198
beyond-all-reason#13 0x5c05ab3a434b in int sol::call_detail::lua_call_wrapper<Rml::Context, bool (Rml::Context::*)(), true, false, true, 0, true, void>::call<bool (Rml::Context::*&)(), Rml::Context&>(lua_State*, bool (Rml::Context::*&)(), Rml::Context&) /build/src/rts/lib/sol2/sol.hpp:18103
beyond-all-reason#14 0x5c05ab3a434b in int sol::call_detail::lua_call_wrapper<Rml::Context, bool (Rml::Context::*)(), true, false, true, 0, true, void>::call<bool (Rml::Context::*&)()>(lua_State*, bool (Rml::Context::*&)()) /build/src/rts/lib/sol2/sol.hpp:18093
beyond-all-reason#15 0x5c05ab3a434b in int sol::call_detail::call_wrapped<Rml::Context, true, false, 0, true, true, bool (Rml::Context::*&)()>(lua_State*, bool (Rml::Context::*&)()) /build/src/rts/lib/sol2/sol.hpp:18506
beyond-all-reason#16 0x5c05ab3a434b in int sol::u_detail::binding<char [7], bool (Rml::Context::*)(), Rml::Context>::call_with_<true, false>(lua_State*, void*) /build/src/rts/lib/sol2/sol.hpp:23023
beyond-all-reason#17 0x5c05ab3a434b in int sol::u_detail::binding<char [7], bool (Rml::Context::*)(), Rml::Context>::call_<true, false>(lua_State*) /build/src/rts/lib/sol2/sol.hpp:23029
beyond-all-reason#18 0x5c05aa5925ba in sol::detail::lua_cfunction_trampoline(lua_State*, int (*)(lua_State*)) /build/src/rts/lib/sol2/sol.hpp:8398
beyond-all-reason#19 0x5c05ab361ccf in int sol::detail::static_trampoline<&(int sol::u_detail::binding<char [7], bool (Rml::Context::*)(), Rml::Context>::call_<true, false>(lua_State*))>(lua_State*) /build/src/rts/lib/sol2/sol.hpp:8423
beyond-all-reason#20 0x5c05ab361ccf in int sol::detail::typed_static_trampoline<int (*)(lua_State*), &(int sol::u_detail::binding<char [7], bool (Rml::Context::*)(), Rml::Context>::call_<true, false>(lua_State*))>(lua_State*) /build/src/rts/lib/sol2/sol.hpp:8490
beyond-all-reason#21 0x5c05ab361ccf in int sol::u_detail::binding<char [7], bool (Rml::Context::*)(), Rml::Context>::call<true, false>(lua_State*) /build/src/rts/lib/sol2/sol.hpp:23034
beyond-all-reason#22 0x5c05ac11b57f in luaD_precall(lua_State*, lua_TValue*, int) /build/src/rts/lib/lua/src/ldo.cpp:320
beyond-all-reason#23 0x5c05ac1499a2 in luaV_execute(lua_State*, int) /build/src/rts/lib/lua/src/lvm.cpp:620
beyond-all-reason#24 0x5c05ac11c484 in luaD_call(lua_State*, lua_TValue*, int) /build/src/rts/lib/lua/src/ldo.cpp:378
beyond-all-reason#25 0x5c05ac1056b8 in f_call /build/src/rts/lib/lua/src/lapi.cpp:812
beyond-all-reason#26 0x5c05ac119bb8 in luaD_rawrunprotected(lua_State*, void (*)(lua_State*, void*), void*) /build/src/rts/lib/lua/src/ldo.cpp:116
beyond-all-reason#27 0x5c05ac11cdc2 in luaD_pcall(lua_State*, void (*)(lua_State*, void*), void*, long, long) /build/src/rts/lib/lua/src/ldo.cpp:464
beyond-all-reason#28 0x5c05ac10d695 in lua_pcall(lua_State*, int, int, int) /build/src/rts/lib/lua/src/lapi.cpp:833
beyond-all-reason#29 0x5c05ac11272f in luaB_pcall /build/src/rts/lib/lua/src/lbaselib.cpp:389
beyond-all-reason#30 0x5c05ac11b57f in luaD_precall(lua_State*, lua_TValue*, int) /build/src/rts/lib/lua/src/ldo.cpp:320
beyond-all-reason#31 0x5c05ac1499a2 in luaV_execute(lua_State*, int) /build/src/rts/lib/lua/src/lvm.cpp:620
beyond-all-reason#32 0x5c05ac11c484 in luaD_call(lua_State*, lua_TValue*, int) /build/src/rts/lib/lua/src/ldo.cpp:378
beyond-all-reason#33 0x5c05ac1056b8 in f_call /build/src/rts/lib/lua/src/lapi.cpp:812
beyond-all-reason#34 0x5c05ac119bb8 in luaD_rawrunprotected(lua_State*, void (*)(lua_State*, void*), void*) /build/src/rts/lib/lua/src/ldo.cpp:116
beyond-all-reason#35 0x5c05ac11cdc2 in luaD_pcall(lua_State*, void (*)(lua_State*, void*), void*, long, long) /build/src/rts/lib/lua/src/ldo.cpp:464
beyond-all-reason#36 0x5c05ac10d695 in lua_pcall(lua_State*, int, int, int) /build/src/rts/lib/lua/src/lapi.cpp:833
beyond-all-reason#37 0x5c05aa1e5175 in ScopedLuaCall /build/src/rts/Lua/LuaHandle.cpp:397
beyond-all-reason#38 0x5c05aa1e5175 in CLuaHandle::RunCallInTraceback(lua_State*, LuaHashString const*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*, int, int, int, bool) /build/src/rts/Lua/LuaHandle.cpp:483
beyond-all-reason#39 0x5c05aa1e6908 in CLuaHandle::RunCallInTraceback(lua_State*, LuaHashString const&, int, int, int, bool) /build/src/rts/Lua/LuaHandle.cpp:494
beyond-all-reason#40 0x5c05aa2102b2 in CLuaHandle::RunCallIn(lua_State*, LuaHashString const&, int, int) /build/src/rts/Lua/LuaHandle.h:425
beyond-all-reason#41 0x5c05aa2102b2 in CLuaHandle::DrawScreenCommon(LuaHashString const&) /build/src/rts/Lua/LuaHandle.cpp:2863
beyond-all-reason#42 0x5c05aa2102b2 in CLuaHandle::DrawScreen() /build/src/rts/Lua/LuaHandle.cpp:2881
beyond-all-reason#43 0x5c05ab9492b0 in CEventHandler::DrawScreen() /build/src/rts/System/EventHandler.cpp:708
beyond-all-reason#44 0x5c05aa24cdef in CLuaInputReceiver::Draw() /build/src/rts/Lua/LuaInputReceiver.cpp:71
beyond-all-reason#45 0x5c05ad971694 in CGame::DrawInputReceivers() /build/src/rts/Game/Game.cpp:1574
beyond-all-reason#46 0x5c05ad98b020 in CGame::Draw() /build/src/rts/Game/Game.cpp:1526
beyond-all-reason#47 0x5c05aba75cce in SpringApp::Update() /build/src/rts/System/SpringApp.cpp:889
beyond-all-reason#48 0x5c05aba8323b in SpringApp::Run() /build/src/rts/System/SpringApp.cpp:927
beyond-all-reason#49 0x5c05ab9edb79 in Run(int, char**) /build/src/rts/System/Main.cpp:51
beyond-all-reason#50 0x5c05a9f546d3 in main /build/src/rts/System/Main.cpp:104
beyond-all-reason#51 0x782b5082a1c9 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
beyond-all-reason#52 0x782b5082a28a in __libc_start_main_impl ../csu/libc-start.c:360
beyond-all-reason#53 0x5c05aa01a049 in _start (/home/gajop/projects/spring-projects/spring-bar/build-linux/install/spring+0x931049) (BuildId: 0100fe9eb07611ab4faec887c8a97ffbdbc3df06)
0x503000460eec is located 28 bytes inside of 32-byte region [0x503000460ed0,0x503000460ef0)
freed by thread T0 (recoil-main) here:
#0 0x782b512fc4d8 in free ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:52
beyond-all-reason#1 0x782b40ef9d00 (/lib/x86_64-linux-gnu/libnvidia-glcore.so.535.274.02+0x14f9d00) (BuildId: 513f593c743a6a4f8ccb0183cb093aa171cef856)
previously allocated by thread T0 (recoil-main) here:
#0 0x782b512fd9c7 in malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:69
beyond-all-reason#1 0x782b40ef9851 (/lib/x86_64-linux-gnu/libnvidia-glcore.so.535.274.02+0x14f9851) (BuildId: 513f593c743a6a4f8ccb0183cb093aa171cef856)
SUMMARY: AddressSanitizer: heap-use-after-free /build/src/rts/Rml/Backends/RmlUi_Renderer_GL3_Recoil.cpp:1727 in RenderInterface_GL3_Recoil::PopLayer()
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
When a Lua event handler calls element.inner_rml = "...", it destroys
all child elements. If Lua holds references to those children and tries
to use them later in the same event handler (e.g., calling SetClass),
it causes a use-after-free.
Solution: Wrap SetInnerRML in the Lua binding to manually remove children
and store them in a deferred deletion list. Children are kept alive until
the event processing completes, preventing use-after-free when Lua
accesses them.
Original ASAN error:
==41062==ERROR: AddressSanitizer: heap-use-after-free on address 0x506000057c28
READ of size 8 at 0x506000057c28 thread T0 (recoil-main)
#0 std::__cxx11::basic_string::size() at /usr/include/c++/13/bits/basic_string.h:1060
beyond-all-reason#6 Rml::ElementStyle::SetClass() at ElementStyle.cpp:255
beyond-all-reason#7 Rml::Element::SetClass() at Element.cpp:297
beyond-all-reason#8 [Sol2/Lua binding call chain]
0x506000057c28 is located 8 bytes inside of 64-byte region freed by thread T0:
beyond-all-reason#7 Rml::ElementStyle::~ElementStyle() at ElementStyle.h:51
beyond-all-reason#11 Rml::Element::~Element() at Element.cpp:151
beyond-all-reason#26 std::vector::clear() at stl_vector.h:1603
beyond-all-reason#41 Rml::Element::~Element() at Element.cpp:148
beyond-all-reason#49 Rml::Element::SetInnerRML() at Element.cpp:1100
beyond-all-reason#52 [Sol2/Lua binding call chain - SetInnerRML called from Lua]
The element was previously allocated during Element::SetClass():
beyond-all-reason#9 Rml::ElementStyle::SetClass() at ElementStyle.cpp:262
beyond-all-reason#10 Rml::Element::SetClass() at Element.cpp:297
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
|
There's also mikke89/RmlUi@master...gajop:RmlUi:fix-asan which is "unfortunately" in the RmlUI itself. Unfortunately because it doesn't seem like BAR has its own version of RmlUi, and I'd rather not put the (quite likely) higher effort it takes to merge upstream. |
| .minFilter = GL_NEAREST, | ||
| .magFilter = GL_LINEAR, | ||
| .wrapModes = std::initializer_list{ GL_REPEAT, GL_CLAMP_TO_EDGE } | ||
| .wrapModes = std::array<int32_t, 3>{ GL_REPEAT, GL_CLAMP_TO_EDGE, GL_REPEAT } // R wrap unused for 2D textures |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where did the 3rd element and its value come from?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's a made up value, something that's valid but shouldn't take any effect when used with 2D textures.
I was thinking whether to express this as std::vector (heap allocation), zero initialized or setting a special enum value and a tiny branch check during use or just rewrite it as a struct that has a count member, and I ended up choosing this since it was simplest and I hoped comment explained it enough.
Happy to change to something else.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not do it like this though? The previous initializer list had 2 elements and apparently it was fine.
- std::array<int32_t, 3>{ GL_REPEAT, GL_CLAMP_TO_EDGE, GL_REPEAT}
+ std::array<int32_t, 2>{ GL_REPEAT, GL_CLAMP_TO_EDGE}If the 3rd is needed because something iterates it to 3 maybe that was the real problem rather than the container being an initializer list?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The array has to be of a fixed size, in this case of 3, just to cover a rare case of 3D textures.
| std::optional<uint32_t> minFilter = std::nullopt; | ||
| std::optional<uint32_t> magFilter = std::nullopt; | ||
| std::optional<std::initializer_list<int32_t>> wrapModes = std::nullopt; | ||
| std::optional<std::array<int32_t, 3>> wrapModes = std::nullopt; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Was std::optional<std::initializer_list<int32_t>> wrapModes flagged by ASAN?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yup, see c205c47
For each commit I included the original ASAN error message.
Apparently std::initializer_list should never be stored, as it doesn't copy anything (it's a view), so the original was just reference to something on the stack.
https://en.cppreference.com/w/cpp/utility/initializer_list.html
std::initializer_list may be implemented as a pair of pointers or pointer and length. Copying a std::initializer_list does not copy the backing array of the corresponding initializer list.
|
The rmlui changes look good to me. |
|
I cherry picked the first two so this will be mostly about Rml |
|
@gajop can you PR mikke89/RmlUi@master...gajop:RmlUi:fix-asan ? |
Is it ok if I sit on this PR a while longer? I want to get to 0 crashes first and I might find a better way to do Rmlui. I dislike that I had to do a change in Rmlui itself, and maybe it's possible to avoid that. Was a bit distracted with some data binding stuff... Will try to get back to this in the coming weeks.. |
|
We've got open PRs from 2023 and nobody else seems to have ran into these issues so a bit of delay shouldn"t hurt. |
|
Yeah fwiw I span regular BAR with Chobby for a couple of minutes of game time and nothing crashed. I didn't try BARs Rmlui version (there's a PR somewhere) The crashes are Rmlui specific. There might be some leaks with the regular game but I didn't investigate deeply (that can have false positives / be too noisy, especially when it comes to end game cleanup) |
|
Tangential to this PR but perhaps the other sanitizer modes could be tried, apparently there's |
|
Last I tried it on a diff project, undefined didn't produce any errors, false or true positive. So at least it shouldn't be a big time waster. Thread sanitizer can also be useful, but it slows down the execution significantly and can have some false positives depending on the threading libraries used. It might just be too slow for Spring, idk. Your best bet is the leak sanitizer that comes with ASAN. Slightly noisy, and only works on program exit, if both you and third party libraries clean up properly, but it can find memory leaks. |
|
Oh and, past I checked they're mutually exclusive (except for Asan + lsan) |
No description provided.