Skip to content

Conversation

@moreginger
Copy link
Contributor

Apparently 24-bit depth is the magic value for amdgpu.
#8

Apparently 24-bit depth is the magic value for amdgpu.
beyond-all-reason#8
@lhog
Copy link
Collaborator

lhog commented Feb 21, 2021

Thanks, let's try 24 bits

@lhog lhog merged commit e3c0d5a into beyond-all-reason:BAR Feb 21, 2021
dennisklein added a commit to dennisklein/spring that referenced this pull request Feb 20, 2024
Passing `nullptr` to `std::memcpy` triggers UB, see
https://en.cppreference.com/w/cpp/string/byte/memcpy.

On Fedora 39 it segfaults (GCC 13, `-O3` build):
```
#0  __memcpy_avx_unaligned_erms () at ../sysdeps/x86_64/multiarch/memmove-vec-unaligned-erms.S:265
beyond-all-reason#1  0x0000000000ccfc9f in spring::ReallocateAlignedMemory
    (ptr=<optimized out>, size=size@entry=536870912, alignment=alignment@entry=64)
    at /spring/rts/System/SpringMem.cpp:38
beyond-all-reason#2  0x0000000000b18ad8 in TexMemPool::Resize (this=0x488a15f0, size=536870912) at /usr/include/c++/13/span:287
beyond-all-reason#3  0x0000000000b0e461 in ITexMemPool::Init (size=size@entry=536870912) at /usr/include/c++/13/bits/unique_ptr.h:199
beyond-all-reason#4  0x0000000000b0ea70 in CBitmap::InitPool (size=536870912, size@entry=512)
    at /spring/rts/Rendering/Textures/Bitmap.cpp:1040
beyond-all-reason#5  0x0000000000c94795 in SpringApp::Init (this=this@entry=0x7ffef74a5830)
    at /spring/rts/System/SpringApp.cpp:258
beyond-all-reason#6  0x0000000000c94f77 in SpringApp::Run (this=this@entry=0x7ffef74a5830)
    at /spring/rts/System/SpringApp.cpp:876
beyond-all-reason#7  0x0000000000c6ceb9 in Run (argc=2, argv=0x7ffef74a5998) at /spring/rts/System/Main.cpp:48
beyond-all-reason#8  0x00007f89e8a4614a in __libc_start_call_main
    (main=main@entry=0xc6cf00 <main(int, char**)>, argc=argc@entry=2, argv=argv@entry=0x7ffef74a5998)
    at ../sysdeps/nptl/libc_start_call_main.h:58
beyond-all-reason#9  0x00007f89e8a4620b in __libc_start_main_impl
    (main=0xc6cf00 <main(int, char**)>, argc=2, argv=0x7ffef74a5998, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7ffef74a5988) at ../csu/libc-start.c:360
beyond-all-reason#10 0x00000000004dc975 in _start ()
```

Even if `posix_memalign` would not segfault, `spring::ReallocateAlignedMemory`
would then return `nullptr` which is clearly not the intented
behaviour.

`clang-tidy` is also flagging the UB:
```
/spring/rts/System/SpringMem.cpp:38:5: warning: Null pointer passed to 2nd parameter expecting 'nonnull' [clang-analyzer-core.NonNullParamChecker]
   38 |     std::memcpy(ptr, newPtr, size);
      |     ^                ~~~~~~
/spring/rts/System/SpringMem.cpp:31:9: note: Assuming the condition is false
   31 |     if (reinterpret_cast<std::uintptr_t>(ptr) % alignment == 0)
      |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/spring/rts/System/SpringMem.cpp:31:5: note: Taking false branch
   31 |     if (reinterpret_cast<std::uintptr_t>(ptr) % alignment == 0)
      |     ^
/spring/rts/System/SpringMem.cpp:35:5: note: 'newPtr' initialized to a null pointer value
   35 |     void* newPtr = nullptr;
      |     ^~~~~~~~~~~~
/spring/rts/System/SpringMem.cpp:36:9: note: Assuming the condition is false
   36 |     if (posix_memalign(&ptr, alignment, size) != 0)
      |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/spring/rts/System/SpringMem.cpp:36:5: note: Taking false branch
   36 |     if (posix_memalign(&ptr, alignment, size) != 0)
      |     ^
/spring/rts/System/SpringMem.cpp:38:5: note: Null pointer passed to 2nd parameter expecting 'nonnull'
   38 |     std::memcpy(ptr, newPtr, size);
      |     ^                ~~~~~~
```
dennisklein added a commit to dennisklein/spring that referenced this pull request Feb 20, 2024
Passing `nullptr` to `std::memcpy` triggers UB, see
https://en.cppreference.com/w/cpp/string/byte/memcpy.

On Fedora 39 it segfaults (GCC 13, `-O3` build):
```
#0  __memcpy_avx_unaligned_erms () at ../sysdeps/x86_64/multiarch/memmove-vec-unaligned-erms.S:265
beyond-all-reason#1  0x0000000000ccfc9f in spring::ReallocateAlignedMemory
    (ptr=<optimized out>, size=size@entry=536870912, alignment=alignment@entry=64)
    at /spring/rts/System/SpringMem.cpp:38
beyond-all-reason#2  0x0000000000b18ad8 in TexMemPool::Resize (this=0x488a15f0, size=536870912) at /usr/include/c++/13/span:287
beyond-all-reason#3  0x0000000000b0e461 in ITexMemPool::Init (size=size@entry=536870912) at /usr/include/c++/13/bits/unique_ptr.h:199
beyond-all-reason#4  0x0000000000b0ea70 in CBitmap::InitPool (size=536870912, size@entry=512)
    at /spring/rts/Rendering/Textures/Bitmap.cpp:1040
beyond-all-reason#5  0x0000000000c94795 in SpringApp::Init (this=this@entry=0x7ffef74a5830)
    at /spring/rts/System/SpringApp.cpp:258
beyond-all-reason#6  0x0000000000c94f77 in SpringApp::Run (this=this@entry=0x7ffef74a5830)
    at /spring/rts/System/SpringApp.cpp:876
beyond-all-reason#7  0x0000000000c6ceb9 in Run (argc=2, argv=0x7ffef74a5998) at /spring/rts/System/Main.cpp:48
beyond-all-reason#8  0x00007f89e8a4614a in __libc_start_call_main
    (main=main@entry=0xc6cf00 <main(int, char**)>, argc=argc@entry=2, argv=argv@entry=0x7ffef74a5998)
    at ../sysdeps/nptl/libc_start_call_main.h:58
beyond-all-reason#9  0x00007f89e8a4620b in __libc_start_main_impl
    (main=0xc6cf00 <main(int, char**)>, argc=2, argv=0x7ffef74a5998, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7ffef74a5988) at ../csu/libc-start.c:360
beyond-all-reason#10 0x00000000004dc975 in _start ()
```

Even if `posix_memalign` would not segfault, `spring::ReallocateAlignedMemory`
would then return `nullptr` which is clearly not the intented
behaviour.

`clang-tidy` is also flagging the UB:
```
/spring/rts/System/SpringMem.cpp:38:5: warning: Null pointer passed to 2nd parameter expecting 'nonnull' [clang-analyzer-core.NonNullParamChecker]
   38 |     std::memcpy(ptr, newPtr, size);
      |     ^                ~~~~~~
/spring/rts/System/SpringMem.cpp:31:9: note: Assuming the condition is false
   31 |     if (reinterpret_cast<std::uintptr_t>(ptr) % alignment == 0)
      |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/spring/rts/System/SpringMem.cpp:31:5: note: Taking false branch
   31 |     if (reinterpret_cast<std::uintptr_t>(ptr) % alignment == 0)
      |     ^
/spring/rts/System/SpringMem.cpp:35:5: note: 'newPtr' initialized to a null pointer value
   35 |     void* newPtr = nullptr;
      |     ^~~~~~~~~~~~
/spring/rts/System/SpringMem.cpp:36:9: note: Assuming the condition is false
   36 |     if (posix_memalign(&ptr, alignment, size) != 0)
      |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/spring/rts/System/SpringMem.cpp:36:5: note: Taking false branch
   36 |     if (posix_memalign(&ptr, alignment, size) != 0)
      |     ^
/spring/rts/System/SpringMem.cpp:38:5: note: Null pointer passed to 2nd parameter expecting 'nonnull'
   38 |     std::memcpy(ptr, newPtr, size);
      |     ^                ~~~~~~
```
dennisklein added a commit to dennisklein/spring that referenced this pull request Feb 20, 2024
Passing `nullptr` to `std::memcpy` triggers UB, see
https://en.cppreference.com/w/cpp/string/byte/memcpy.

On Fedora 39 it segfaults (GCC 13, `-O3` build):
```
#0  __memcpy_avx_unaligned_erms () at ../sysdeps/x86_64/multiarch/memmove-vec-unaligned-erms.S:265
beyond-all-reason#1  0x0000000000ccfc9f in spring::ReallocateAlignedMemory
    (ptr=<optimized out>, size=size@entry=536870912, alignment=alignment@entry=64)
    at /spring/rts/System/SpringMem.cpp:38
beyond-all-reason#2  0x0000000000b18ad8 in TexMemPool::Resize (this=0x488a15f0, size=536870912) at /usr/include/c++/13/span:287
beyond-all-reason#3  0x0000000000b0e461 in ITexMemPool::Init (size=size@entry=536870912) at /usr/include/c++/13/bits/unique_ptr.h:199
beyond-all-reason#4  0x0000000000b0ea70 in CBitmap::InitPool (size=536870912, size@entry=512)
    at /spring/rts/Rendering/Textures/Bitmap.cpp:1040
beyond-all-reason#5  0x0000000000c94795 in SpringApp::Init (this=this@entry=0x7ffef74a5830)
    at /spring/rts/System/SpringApp.cpp:258
beyond-all-reason#6  0x0000000000c94f77 in SpringApp::Run (this=this@entry=0x7ffef74a5830)
    at /spring/rts/System/SpringApp.cpp:876
beyond-all-reason#7  0x0000000000c6ceb9 in Run (argc=2, argv=0x7ffef74a5998) at /spring/rts/System/Main.cpp:48
beyond-all-reason#8  0x00007f89e8a4614a in __libc_start_call_main
    (main=main@entry=0xc6cf00 <main(int, char**)>, argc=argc@entry=2, argv=argv@entry=0x7ffef74a5998)
    at ../sysdeps/nptl/libc_start_call_main.h:58
beyond-all-reason#9  0x00007f89e8a4620b in __libc_start_main_impl
    (main=0xc6cf00 <main(int, char**)>, argc=2, argv=0x7ffef74a5998, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7ffef74a5988) at ../csu/libc-start.c:360
beyond-all-reason#10 0x00000000004dc975 in _start ()
```

Even if some systems do not segfault, `spring::ReallocateAlignedMemory`
would then return `nullptr` which is clearly not the intented
behaviour.

`clang-tidy` is also flagging the UB:
```
/spring/rts/System/SpringMem.cpp:38:5: warning: Null pointer passed to 2nd parameter expecting 'nonnull' [clang-analyzer-core.NonNullParamChecker]
   38 |     std::memcpy(ptr, newPtr, size);
      |     ^                ~~~~~~
/spring/rts/System/SpringMem.cpp:31:9: note: Assuming the condition is false
   31 |     if (reinterpret_cast<std::uintptr_t>(ptr) % alignment == 0)
      |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/spring/rts/System/SpringMem.cpp:31:5: note: Taking false branch
   31 |     if (reinterpret_cast<std::uintptr_t>(ptr) % alignment == 0)
      |     ^
/spring/rts/System/SpringMem.cpp:35:5: note: 'newPtr' initialized to a null pointer value
   35 |     void* newPtr = nullptr;
      |     ^~~~~~~~~~~~
/spring/rts/System/SpringMem.cpp:36:9: note: Assuming the condition is false
   36 |     if (posix_memalign(&ptr, alignment, size) != 0)
      |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/spring/rts/System/SpringMem.cpp:36:5: note: Taking false branch
   36 |     if (posix_memalign(&ptr, alignment, size) != 0)
      |     ^
/spring/rts/System/SpringMem.cpp:38:5: note: Null pointer passed to 2nd parameter expecting 'nonnull'
   38 |     std::memcpy(ptr, newPtr, size);
      |     ^                ~~~~~~
```
lhog pushed a commit that referenced this pull request Feb 21, 2024
Passing `nullptr` to `std::memcpy` triggers UB, see
https://en.cppreference.com/w/cpp/string/byte/memcpy.

On Fedora 39 it segfaults (GCC 13, `-O3` build):
```
#0  __memcpy_avx_unaligned_erms () at ../sysdeps/x86_64/multiarch/memmove-vec-unaligned-erms.S:265
#1  0x0000000000ccfc9f in spring::ReallocateAlignedMemory
    (ptr=<optimized out>, size=size@entry=536870912, alignment=alignment@entry=64)
    at /spring/rts/System/SpringMem.cpp:38
#2  0x0000000000b18ad8 in TexMemPool::Resize (this=0x488a15f0, size=536870912) at /usr/include/c++/13/span:287
#3  0x0000000000b0e461 in ITexMemPool::Init (size=size@entry=536870912) at /usr/include/c++/13/bits/unique_ptr.h:199
#4  0x0000000000b0ea70 in CBitmap::InitPool (size=536870912, size@entry=512)
    at /spring/rts/Rendering/Textures/Bitmap.cpp:1040
#5  0x0000000000c94795 in SpringApp::Init (this=this@entry=0x7ffef74a5830)
    at /spring/rts/System/SpringApp.cpp:258
#6  0x0000000000c94f77 in SpringApp::Run (this=this@entry=0x7ffef74a5830)
    at /spring/rts/System/SpringApp.cpp:876
#7  0x0000000000c6ceb9 in Run (argc=2, argv=0x7ffef74a5998) at /spring/rts/System/Main.cpp:48
#8  0x00007f89e8a4614a in __libc_start_call_main
    (main=main@entry=0xc6cf00 <main(int, char**)>, argc=argc@entry=2, argv=argv@entry=0x7ffef74a5998)
    at ../sysdeps/nptl/libc_start_call_main.h:58
#9  0x00007f89e8a4620b in __libc_start_main_impl
    (main=0xc6cf00 <main(int, char**)>, argc=2, argv=0x7ffef74a5998, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7ffef74a5988) at ../csu/libc-start.c:360
#10 0x00000000004dc975 in _start ()
```

Even if some systems do not segfault, `spring::ReallocateAlignedMemory`
would then return `nullptr` which is clearly not the intented
behaviour.

`clang-tidy` is also flagging the UB:
```
/spring/rts/System/SpringMem.cpp:38:5: warning: Null pointer passed to 2nd parameter expecting 'nonnull' [clang-analyzer-core.NonNullParamChecker]
   38 |     std::memcpy(ptr, newPtr, size);
      |     ^                ~~~~~~
/spring/rts/System/SpringMem.cpp:31:9: note: Assuming the condition is false
   31 |     if (reinterpret_cast<std::uintptr_t>(ptr) % alignment == 0)
      |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/spring/rts/System/SpringMem.cpp:31:5: note: Taking false branch
   31 |     if (reinterpret_cast<std::uintptr_t>(ptr) % alignment == 0)
      |     ^
/spring/rts/System/SpringMem.cpp:35:5: note: 'newPtr' initialized to a null pointer value
   35 |     void* newPtr = nullptr;
      |     ^~~~~~~~~~~~
/spring/rts/System/SpringMem.cpp:36:9: note: Assuming the condition is false
   36 |     if (posix_memalign(&ptr, alignment, size) != 0)
      |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/spring/rts/System/SpringMem.cpp:36:5: note: Taking false branch
   36 |     if (posix_memalign(&ptr, alignment, size) != 0)
      |     ^
/spring/rts/System/SpringMem.cpp:38:5: note: Null pointer passed to 2nd parameter expecting 'nonnull'
   38 |     std::memcpy(ptr, newPtr, size);
      |     ^                ~~~~~~
```
gajop added a commit to gajop/bar-spring that referenced this pull request Nov 25, 2025
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>
gajop added a commit to gajop/bar-spring that referenced this pull request Nov 25, 2025
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>
gajop added a commit to gajop/bar-spring that referenced this pull request Nov 25, 2025
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>
lhog pushed a commit that referenced this pull request Nov 26, 2025
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
    #1 0x55c80ddb721c in GL::Texture2D::Texture2D(int, int, unsigned int, GL::TextureCreationParams const&, bool) /build/src/rts/Rendering/Textures/Texture.cpp:133
    #2 0x55c80daf951c in CHeightTexture::CHeightTexture() /build/src/rts/Rendering/Map/InfoTexture/Modern/Height.cpp:49
    #3 0x55c80db02149 in CInfoTextureHandler::CInfoTextureHandler() /build/src/rts/Rendering/Map/InfoTexture/Modern/InfoTextureHandler.cpp:26
    #4 0x55c80daec745 in std::__detail::_MakeUniq<CInfoTextureHandler>::__single_object std::make_unique<CInfoTextureHandler>() /usr/include/c++/13/bits/unique_ptr.h:1070
    #5 0x55c80daec745 in IInfoTextureHandler::Create() /build/src/rts/Rendering/Map/InfoTexture/IInfoTextureHandler.cpp:25
    #6 0x55c80df3b1d1 in CWorldDrawer::InitPost() const /build/src/rts/Rendering/WorldDrawer.cpp:111
    #7 0x55c8107955f2 in CGame::PostLoadRendering() /build/src/rts/Game/Game.cpp:756
    #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
    #9 0x55c81084498e in CLoadScreen::Init() /build/src/rts/Game/LoadScreen.cpp:146
    #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
    #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
    #12 0x55c8108758d6 in CPreGame::UpdateClientNet() /build/src/rts/Game/PreGame.cpp:470
    #13 0x55c810876b6f in CPreGame::Update() /build/src/rts/Game/PreGame.cpp:241
    #14 0x55c80e883938 in SpringApp::Update() /build/src/rts/System/SpringApp.cpp:886
    #15 0x55c80e890ffb in SpringApp::Run() /build/src/rts/System/SpringApp.cpp:927
    #16 0x55c80e7fb939 in Run(int, char**) /build/src/rts/System/Main.cpp:51
    #17 0x55c80cd63653 in main /build/src/rts/System/Main.cpp:104
    #18 0x74d30642a1c9 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
    #19 0x74d30642a28a in __libc_start_main_impl ../csu/libc-start.c:360
    #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>
lhog pushed a commit that referenced this pull request Nov 28, 2025
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
    #1 0x55c80ddb721c in GL::Texture2D::Texture2D(int, int, unsigned int, GL::TextureCreationParams const&, bool) /build/src/rts/Rendering/Textures/Texture.cpp:133
    #2 0x55c80daf951c in CHeightTexture::CHeightTexture() /build/src/rts/Rendering/Map/InfoTexture/Modern/Height.cpp:49
    #3 0x55c80db02149 in CInfoTextureHandler::CInfoTextureHandler() /build/src/rts/Rendering/Map/InfoTexture/Modern/InfoTextureHandler.cpp:26
    #4 0x55c80daec745 in std::__detail::_MakeUniq<CInfoTextureHandler>::__single_object std::make_unique<CInfoTextureHandler>() /usr/include/c++/13/bits/unique_ptr.h:1070
    #5 0x55c80daec745 in IInfoTextureHandler::Create() /build/src/rts/Rendering/Map/InfoTexture/IInfoTextureHandler.cpp:25
    #6 0x55c80df3b1d1 in CWorldDrawer::InitPost() const /build/src/rts/Rendering/WorldDrawer.cpp:111
    #7 0x55c8107955f2 in CGame::PostLoadRendering() /build/src/rts/Game/Game.cpp:756
    #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
    #9 0x55c81084498e in CLoadScreen::Init() /build/src/rts/Game/LoadScreen.cpp:146
    #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
    #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
    #12 0x55c8108758d6 in CPreGame::UpdateClientNet() /build/src/rts/Game/PreGame.cpp:470
    #13 0x55c810876b6f in CPreGame::Update() /build/src/rts/Game/PreGame.cpp:241
    #14 0x55c80e883938 in SpringApp::Update() /build/src/rts/System/SpringApp.cpp:886
    #15 0x55c80e890ffb in SpringApp::Run() /build/src/rts/System/SpringApp.cpp:927
    #16 0x55c80e7fb939 in Run(int, char**) /build/src/rts/System/Main.cpp:51
    #17 0x55c80cd63653 in main /build/src/rts/System/Main.cpp:104
    #18 0x74d30642a1c9 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
    #19 0x74d30642a28a in __libc_start_main_impl ../csu/libc-start.c:360
    #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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants