ThePhD / sol2

Sol3 (sol2 v3.0) - a C++ <-> Lua API wrapper with advanced features and top notch performance - is here, and it's great! Documentation:
http://sol2.rtfd.io/
MIT License
4.16k stars 504 forks source link

ASan heap-use-after-free in clear_usertype_registry_names when state is stored with static storage #1406

Open mackal opened 1 year ago

mackal commented 1 year ago

In porting a project to sol2 we have a LuaParser class that uses singleton pattern for now. ASan doesn't report problems when it's just tables, enums, etc, just happens once I add a usertype.

This happened on my local machine using GCC 12 and my test debian VM using GCC 10. Clang 14 on my local machine also has the same issue. Tried with lua 5.4, 5.1, and LuaJIT 2.1.0_beta3_p20220613 (whatever that is on Gentoo). Sol version was 3.3.0 and tried current develop HEAD

Here is a repo:

#include <sol/sol.hpp>

// build: g++ -fsanitize=address -I./include $(pkg-config --libs --cflags lua5.4) heap-after-free.cpp -o heap

static sol::state state;

struct A { };

int main(int argc, char *argv[])
{
    state.new_usertype<A>("A");

    return 0;
}

ASan output:

=================================================================
==1287530==ERROR: AddressSanitizer: heap-use-after-free on address 0x603000000a30 at pc 0x7f16e84999c0 bp 0x7fff8442b6e0 sp 0x7fff8442ae88
READ of size 1 at 0x603000000a30 thread T0
    #0 0x7f16e84999bf  (/usr/lib/gcc/x86_64-pc-linux-gnu/12.2.0/libasan.so.8+0xab9bf)
    #1 0x7f16e83cf32d  (/usr/lib64/liblua5.4.so.0+0x2732d)
    #2 0x7f16e83b0fd1  (/usr/lib64/liblua5.4.so.0+0x8fd1)
    #3 0x560f7206596e in void sol::stack::field_setter<char const*, false, false, void>::set<char const*, sol::lua_nil_t const&>(lua_State*, char const*&&, sol::lua_nil_t const&, int) (/home/USERNAME/test/sol2/heap2+0x4496e)
    #4 0x560f720624dc in void sol::stack::set_field<false, false, char const*, sol::lua_nil_t const&>(lua_State*, char const*&&, sol::lua_nil_t const&, int) (/home/USERNAME/test/sol2/heap2+0x414dc)
    #5 0x560f7205ca42 in void sol::u_detail::clear_usertype_registry_names<A>(lua_State*) (/home/USERNAME/test/sol2/heap2+0x3ba42)
    #6 0x560f7205cde1 in int sol::u_detail::destroy_usertype_storage<A>(lua_State*) (/home/USERNAME/test/sol2/heap2+0x3bde1)
    #7 0x7f16e83bec6d  (/usr/lib64/liblua5.4.so.0+0x16c6d)
    #8 0x7f16e83befe6  (/usr/lib64/liblua5.4.so.0+0x16fe6)
    #9 0x7f16e83bdf0a  (/usr/lib64/liblua5.4.so.0+0x15f0a)
    #10 0x7f16e83bf3bf  (/usr/lib64/liblua5.4.so.0+0x173bf)
    #11 0x7f16e83c07a5  (/usr/lib64/liblua5.4.so.0+0x187a5)
    #12 0x7f16e83c25f7  (/usr/lib64/liblua5.4.so.0+0x1a5f7)
    #13 0x7f16e83ce40e  (/usr/lib64/liblua5.4.so.0+0x2640e)
    #14 0x560f7202a5b5 in sol::detail::state_deleter::operator()(lua_State*) const (/home/USERNAME/test/sol2/heap2+0x95b5)
    #15 0x560f7203da26 in std::unique_ptr<lua_State, sol::detail::state_deleter>::~unique_ptr() (/home/USERNAME/test/sol2/heap2+0x1ca26)
    #16 0x560f720380c3 in sol::state::~state() (/home/USERNAME/test/sol2/heap2+0x170c3)
    #17 0x7f16e7ed0a27  (/lib64/libc.so.6+0x40a27)
    #18 0x7f16e7ed0b29 in exit (/lib64/libc.so.6+0x40b29)
    #19 0x7f16e7eb92d8  (/lib64/libc.so.6+0x292d8)
    #20 0x7f16e7eb9394 in __libc_start_main (/lib64/libc.so.6+0x29394)
    #21 0x560f720299b0 in _start (/home/USERNAME/test/sol2/heap2+0x89b0)

0x603000000a30 is located 0 bytes inside of 17-byte region [0x603000000a30,0x603000000a41)
freed by thread T0 here:
    #0 0x7f16e84ac6c8 in operator delete(void*) (/usr/lib/gcc/x86_64-pc-linux-gnu/12.2.0/libasan.so.8+0xbe6c8)
    #1 0x7f16e7ed0a27  (/lib64/libc.so.6+0x40a27)

previously allocated by thread T0 here:
    #0 0x7f16e84abb88 in operator new(unsigned long) (/usr/lib/gcc/x86_64-pc-linux-gnu/12.2.0/libasan.so.8+0xbdb88)
    #1 0x7f16e82d0169 in void std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_construct<char*>(char*, char*, std::forward_iterator_tag) (/usr/lib/gcc/x86_64-pc-linux-gnu/12.2.0/libstdc++.so.6+0x146169)

SUMMARY: AddressSanitizer: heap-use-after-free (/usr/lib/gcc/x86_64-pc-linux-gnu/12.2.0/libasan.so.8+0xab9bf) 
Shadow bytes around the buggy address:
  0x0c067fff80f0: fa fa 00 00 00 03 fa fa 00 00 00 07 fa fa 00 00
  0x0c067fff8100: 00 07 fa fa 00 00 00 05 fa fa 00 00 00 05 fa fa
  0x0c067fff8110: 00 00 00 06 fa fa 00 00 00 06 fa fa fd fd fd fa
  0x0c067fff8120: fa fa 00 00 00 fa fa fa fd fd fd fa fa fa fd fd
  0x0c067fff8130: fd fa fa fa fd fd fd fa fa fa 00 00 00 07 fa fa
=>0x0c067fff8140: fd fd fd fd fa fa[fd]fd fd fa fa fa 00 00 00 06
  0x0c067fff8150: fa fa 00 00 00 fa fa fa 00 00 00 02 fa fa 00 00
  0x0c067fff8160: 00 05 fa fa fd fd fd fa fa fa 00 00 00 03 fa fa
  0x0c067fff8170: 00 00 00 07 fa fa fd fd fd fa fa fa fd fd fd fa
  0x0c067fff8180: fa fa 00 00 00 07 fa fa 00 00 00 00 fa fa 00 00
  0x0c067fff8190: 00 04 fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==1287530==ABORTING
diath commented 5 months ago

Also have the same problem:

==108110==ERROR: AddressSanitizer: heap-use-after-free on address 0x503000028c30 at pc 0x5f9bc4fb45c0 bp 0x7ffdf0c89d90 sp 0x7ffdf0c89550
READ of size 1 at 0x503000028c30 thread T0
    #0 0x5f9bc4fb45bf in strcmp.part.0 asan_interceptors.cpp.o
    #1 0x72ad802b0d85  (/usr/lib/liblua.so.5.4+0x18d85) (BuildId: 840cd7592307bf9ff008351d1bcf370b6579562b)
    #2 0x72ad802a8135  (/usr/lib/liblua.so.5.4+0x10135) (BuildId: 840cd7592307bf9ff008351d1bcf370b6579562b)
    #3 0x5f9bc52c3ff8 in void sol::stack::field_setter<char const*, false, false, void>::set<char const*, sol::lua_nil_t const&>(lua_State*, char const*&&, sol::lua_nil_t const&, int) /usr/include/sol/stack_field.hpp:223:7
    #4 0x5f9bc52c3ff8 in void sol::stack::set_field<false, false, char const*, sol::lua_nil_t const&>(lua_State*, char const*&&, sol::lua_nil_t const&, int) /usr/include/sol/stack_core.hpp:1250:59
    #5 0x5f9bc52c3ff8 in void sol::u_detail::clear_usertype_registry_names<AuthProtocol>(lua_State*) /usr/include/sol/usertype_storage.hpp:821:3
    #6 0x5f9bc52c41c5 in int sol::u_detail::destroy_usertype_storage<AuthProtocol>(lua_State*) /usr/include/sol/usertype_storage.hpp:831:3
    #7 0x72ad802aa6da  (/usr/lib/liblua.so.5.4+0x126da) (BuildId: 840cd7592307bf9ff008351d1bcf370b6579562b)
    #8 0x72ad802ad1f5  (/usr/lib/liblua.so.5.4+0x151f5) (BuildId: 840cd7592307bf9ff008351d1bcf370b6579562b)
    #9 0x72ad802a5573  (/usr/lib/liblua.so.5.4+0xd573) (BuildId: 840cd7592307bf9ff008351d1bcf370b6579562b)
    #10 0x72ad802c4f33  (/usr/lib/liblua.so.5.4+0x2cf33) (BuildId: 840cd7592307bf9ff008351d1bcf370b6579562b)
    #11 0x72ad802a1948  (/usr/lib/liblua.so.5.4+0x9948) (BuildId: 840cd7592307bf9ff008351d1bcf370b6579562b)
    #12 0x72ad802a9327  (/usr/lib/liblua.so.5.4+0x11327) (BuildId: 840cd7592307bf9ff008351d1bcf370b6579562b)
    #13 0x72ad802aea2e  (/usr/lib/liblua.so.5.4+0x16a2e) (BuildId: 840cd7592307bf9ff008351d1bcf370b6579562b)
    #14 0x5f9bc5305212 in sol::detail::state_deleter::operator()(lua_State*) const /usr/include/sol/raii.hpp:70:5
    #15 0x5f9bc5305212 in std::unique_ptr<lua_State, sol::detail::state_deleter>::~unique_ptr() /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/13.2.1/../../../../include/c++/13.2.1/bits/unique_ptr.h:404:4
    #16 0x5f9bc5305212 in sol::state::~state() /usr/include/sol/state.hpp:58:3
    #17 0x72ad7fa5cb35  (/usr/lib/libc.so.6+0x3eb35) (BuildId: c0caa0b7709d3369ee575fcd7d7d0b0fc48733af)
    #18 0x72ad7fa5cc7f in exit (/usr/lib/libc.so.6+0x3ec7f) (BuildId: c0caa0b7709d3369ee575fcd7d7d0b0fc48733af)
    #19 0x72ad7fa43cd6  (/usr/lib/libc.so.6+0x25cd6) (BuildId: c0caa0b7709d3369ee575fcd7d7d0b0fc48733af)
    #20 0x72ad7fa43d89 in __libc_start_main (/usr/lib/libc.so.6+0x25d89) (BuildId: c0caa0b7709d3369ee575fcd7d7d0b0fc48733af)
    #21 0x5f9bc4eff124 in _start (/mnt/Dev/Projects/games/echoes/client/build/client+0x1de124) (BuildId: 00e57742313dd2cf1b1d889d0383cba0da3c8da9)

0x503000028c30 is located 0 bytes inside of 17-byte region [0x503000028c30,0x503000028c41)
freed by thread T0 here:
    #0 0x5f9bc5036e1a in operator delete(void*) (/mnt/Dev/Projects/games/echoes/client/build/client+0x315e1a) (BuildId: 00e57742313dd2cf1b1d889d0383cba0da3c8da9)
    #1 0x72ad7fa5cb35  (/usr/lib/libc.so.6+0x3eb35) (BuildId: c0caa0b7709d3369ee575fcd7d7d0b0fc48733af)

previously allocated by thread T0 here:
    #0 0x5f9bc5036382 in operator new(unsigned long) (/mnt/Dev/Projects/games/echoes/client/build/client+0x315382) (BuildId: 00e57742313dd2cf1b1d889d0383cba0da3c8da9)
    #1 0x5f9bc503d87d in void std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>::_M_construct<char*>(char*, char*, std::forward_iterator_tag) /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/13.2.1/../../../../include/c++/13.2.1/bits/basic_string.tcc:225:14
    #2 0x5f9bc52c3c28 in sol::usertype_traits<AuthProtocol>::metatable[abi:cxx11]() /usr/include/sol/usertype_traits.hpp:42:33
    #3 0x5f9bc52c24be in int sol::u_detail::register_usertype<AuthProtocol, (sol::automagic_flags)511>(lua_State*, sol::automagic_enrollments) /usr/include/sol/usertype_storage.hpp:1013:51
    #4 0x5f9bc52c18a4 in sol::basic_usertype<AuthProtocol, sol::basic_reference<false>> sol::basic_table_core<true, sol::basic_reference<false>>::new_usertype<AuthProtocol, char const (&) [13], (sol::automagic_flags)511>(char const (&) [13], sol::constant_automagic_enrollments<(sol::automagic_flags)511>) /usr/include/sol/table.hpp:44:18
    #5 0x5f9bc52058c5 in sol::basic_usertype<AuthProtocol, sol::basic_reference<false>> sol::basic_table_core<true, sol::basic_reference<false>>::new_usertype<AuthProtocol, char const (&) [13], sol::factory_wrapper<Script::registerFunctions()::$_3>, sol::base_list<> const&, sol::base_list<Protocol>, char const (&) [10], void (AuthProtocol::*)(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&), char const (&) [25], void (AuthProtocol::*)(), char const (&) [20], void (AuthProtocol::*)(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&, unsigned char, bool, bool, bool), char const (&) [20], void (AuthProtocol::*)(unsigned long), char const (&) [8], std::function<void (bool, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&)> AuthProtocol::*, char const (&) [16], std::function<void (std::vector<CharacterListEntry, std::allocator<CharacterListEntry>> const&)> AuthProtocol::*, void>(char const (&) [13], sol::factory_wrapper<Script::registerFunctions()::$_3>&&, sol::base_list<> const&, sol::base_list<Protocol>&&, char const (&) [10], void (AuthProtocol::*&&)(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&), char const (&) [25], void (AuthProtocol::*&&)(), char const (&) [20], void (AuthProtocol::*&&)(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&, unsigned char, bool, bool, bool), char const (&) [20], void (AuthProtocol::*&&)(unsigned long), char const (&) [8], std::function<void (bool, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&)> AuthProtocol::*&&, char const (&) [16], std::function<void (std::vector<CharacterListEntry, std::allocator<CharacterListEntry>> const&)> AuthProtocol::*&&) /usr/include/sol/table.hpp:70:30
    #6 0x5f9bc52058c5 in sol::basic_usertype<AuthProtocol, sol::basic_reference<false>> sol::state_view::new_usertype<AuthProtocol, char const (&) [13], sol::factory_wrapper<Script::registerFunctions()::$_3>, sol::base_list<> const&, sol::base_list<Protocol>, char const (&) [10], void (AuthProtocol::*)(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&), char const (&) [25], void (AuthProtocol::*)(), char const (&) [20], void (AuthProtocol::*)(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&, unsigned char, bool, bool, bool), char const (&) [20], void (AuthProtocol::*)(unsigned long), char const (&) [8], std::function<void (bool, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&)> AuthProtocol::*, char const (&) [16], std::function<void (std::vector<CharacterListEntry, std::allocator<CharacterListEntry>> const&)> AuthProtocol::*>(char const (&) [13], sol::factory_wrapper<Script::registerFunctions()::$_3>&&, sol::base_list<> const&, sol::base_list<Protocol>&&, char const (&) [10], void (AuthProtocol::*&&)(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&), char const (&) [25], void (AuthProtocol::*&&)(), char const (&) [20], void (AuthProtocol::*&&)(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&, unsigned char, bool, bool, bool), char const (&) [20], void (AuthProtocol::*&&)(unsigned long), char const (&) [8], std::function<void (bool, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&)> AuthProtocol::*&&, char const (&) [16], std::function<void (std::vector<CharacterListEntry, std::allocator<CharacterListEntry>> const&)> AuthProtocol::*&&) /usr/include/sol/state_view.hpp:785:18
    #7 0x5f9bc52058c5 in Script::registerFunctions() /mnt/Dev/Projects/games/echoes/client/src/script.cpp:129:8
    #8 0x5f9bc515c097 in Script::init() /mnt/Dev/Projects/games/echoes/shared/src/script/script.cpp:74:2
    #9 0x5f9bc52fd608 in Window::run(std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>>> const&) /mnt/Dev/Projects/games/echoes/client/src/engine/window.cpp:40:21
    #10 0x5f9bc51fd5ea in main /mnt/Dev/Projects/games/echoes/client/src/main.cpp:9:28
    #11 0x72ad7fa43ccf  (/usr/lib/libc.so.6+0x25ccf) (BuildId: c0caa0b7709d3369ee575fcd7d7d0b0fc48733af)

SUMMARY: AddressSanitizer: heap-use-after-free asan_interceptors.cpp.o in strcmp.part.0
Shadow bytes around the buggy address:
  0x503000028980: fa fa 00 00 00 04 fa fa fd fd fd fd fa fa fd fd
  0x503000028a00: fd fa fa fa 00 00 00 fa fa fa fd fd fd fa fa fa
  0x503000028a80: fd fd fd fd fa fa fd fd fd fa fa fa fd fd fd fd
  0x503000028b00: fa fa fd fd fd fd fa fa fd fd fd fd fa fa fd fd
  0x503000028b80: fd fa fa fa fd fd fd fd fa fa fd fd fd fd fa fa
=>0x503000028c00: fd fd fd fd fa fa[fd]fd fd fa fa fa 00 00 00 fa
  0x503000028c80: fa fa fd fd fd fa fa fa fd fd fd fd fa fa fd fd
  0x503000028d00: fd fa fa fa 00 00 00 fa fa fa fd fd fd fd fa fa
  0x503000028d80: 00 00 00 fa fa fa fd fd fd fd fa fa 00 00 00 fa
  0x503000028e00: fa fa fd fd fd fd fa fa fd fd fd fa fa fa 00 00
  0x503000028e80: 00 fa fa fa 00 00 03 fa fa fa fd fd fd fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==108110==ABORTING