sol2 icon indicating copy to clipboard operation
sol2 copied to clipboard

[LuaJIT] Invalid method call causes segmentation fault

Open naquad opened this issue 6 months ago • 3 comments

Versions LuaJIT 2.1.1748459687-1 Sol2 3.5.0-1 Clang 20.1.6 Linux x86-64

Minimal reproduction code

#include <iostream>

#define SOL_ALL_SAFETIES_ON 1
#define SOL_CHECK_ARGUMENTS 1
// Sol2 for Lua bindings
#include <sol/sol.hpp>

class SomeClass {
public:
    int val;

    SomeClass() = default;
    SomeClass(int val) : val(val) {}
    SomeClass(SomeClass&& other) noexcept = default;
    SomeClass& operator=(SomeClass&&) noexcept = default;
    SomeClass& operator=(const SomeClass&) = default;

    void say_hi() {
        std::cout << "Method is working as expected\n";
    }
};

int main(int argc, char* argv[]) {
    sol::state lua;
    lua.open_libraries(sol::lib::base);

    lua.new_usertype<SomeClass>(
        "SomeClass",
        sol::no_constructor,

        "say_hi", &SomeClass::say_hi,

        "check", [](SomeClass& me, sol::this_state L) -> sol::object {
            return sol::make_object(L, me.val);
        }
    );

    lua["tpl"] = std::move(SomeClass(123));

    lua.script(R"(
       -- this fails properly
       local ok, err = pcall(function() tpl.say_hi() end)
       assert(not ok)

       -- and this segfaults
       tpl.check() -- <---- mind the "." instead of ":"
    )");
    return 0;
}

Expected result

Lua error / Sol2 exception when trying to call a method as a function.

Actual result

Segmentation fault.

GDB Backtrace
 895              }
 896  #endif // interop extensibility
 897              tracking.use(1);
 898              void* rawdata = detail::align_usertype_pointer(memory);
 899              void** pudata = static_cast(rawdata);
 900              void* udata = *pudata; // Backtrace:

#0 0x0000555555581bba in sol::stack::unqualified_getter<sol::detail::as_value_tag<SomeClass>, void>::get_no_lua_nil (L=0x7ffff7f9c380, index=1, tracking=...) at /usr/include/sol/stack_get_unqualified.hpp:900 #1 0x0000555555581b63 in sol::stack::unqualified_getter<sol::detail::as_value_tag<SomeClass>, void>::get (L=0x7ffff7f9c380, index=1, tracking=...) at /usr/include/sol/stack_get_unqualified.hpp:929 #2 0x0000555555581b14 in sol::stack::stack_detail::unchecked_unqualified_get<sol::detail::as_value_tag<SomeClass> > (L=0x7ffff7f9c380, index=1, tracking=...) at /usr/include/sol/stack_core.hpp:712 #3 0x0000555555581ad3 in sol::stack::unqualified_getter<SomeClass, void>::get (L=0x7ffff7f9c380, index=1, tracking=...) at /usr/include/sol/stack_get_unqualified.hpp:181 #4 0x0000555555581514 in sol::stack::stack_detail::unchecked_unqualified_get<SomeClass> (L=0x7ffff7f9c380, index=1, tracking=...) at /usr/include/sol/stack_core.hpp:712 #5 0x000055555558bd93 in sol::stack::qualified_getter<SomeClass&, void>::get (L=0x7ffff7f9c380, index=1, tracking=...) at /usr/include/sol/stack_get_unqualified.hpp:256 #6 0x000055555558bd44 in sol::stack::stack_detail::unchecked_get<SomeClass&> (L=0x7ffff7f9c380, index=1, tracking=...) at /usr/include/sol/stack_core.hpp:723 #7 0x000055555558ba63 in sol::stack::stack_detail::unchecked_get_arg<SomeClass&> (L_=0x7ffff7f9c380, index_=1, tracking_=...) at /usr/include/sol/stack_core.hpp:1295 #8 0x000055555555c226 in sol::stack::stack_detail::eval<true, SomeClass&, sol::this_state, 0ul, 1ul, sol::argument_handler<sol::types<sol::basic_object<sol::basic_reference >, SomeClass&, sol::this_state> >&, sol::wrapper<sol::basic_object<sol::basic_reference > ()(SomeClass&, sol::this_state), void>::caller, sol::basic_object<sol::basic_reference > (&)(SomeClass&, sol::this_state)> (L_=0x7ffff7f9c380, start_index_=1, handler_=..., tracking_=..., fx_=..., fxargs_=@0x7fffffffcd20: 0x55555555c0e0 <main::$0::invoke(SomeClass&, sol::this_state)>) at /usr/include/sol/stack.hpp:182 #9 0x000055555558ba1a in sol::stack::stack_detail::call<true, 0ul, 1ul, sol::basic_object<sol::basic_reference >, SomeClass&, sol::this_state, sol::wrapper<sol::basic_object<sol::basic_reference > ()(SomeClass&, sol::this_state), void>::caller, sol::basic_object<sol::basic_reference > (&)(SomeClass&, sol::this_state)> (argument_types=..., argument_indices=..., L=0x7ffff7f9c380, start_index_=1, fx_=..., args_=@0x7fffffffcd20: 0x55555555c0e0 <main::$_0::__invoke(SomeClass&, sol::this_state)>) at /usr/include/sol/stack.hpp:204 #10 0x000055555558b976 in sol::stack::call<true, sol::basic_object<sol::basic_reference >, SomeClass&, sol::this_state, sol::wrapper<sol::basic_object<sol::basic_reference > ()(SomeClass&, sol::this_state), void>::caller, sol::basic_object<sol::basic_reference > (&)(SomeClass&, sol::this_state)> (tr=..., ta=..., L=0x7ffff7f9c380, start=1, fx=..., args=@0x7fffffffcd20: 0x55555555c0e0 <main::$_0::__invoke(SomeClass&, sol::this_state)>) at /usr/include/sol/stack.hpp:233 #11 0x000055555558b8bc in sol::stack::call_into_lua<true, true, sol::basic_object<sol::basic_reference >, , SomeClass&, sol::this_state, sol::wrapper<sol::basic_object<sol::basic_reference > ()(SomeClass&, sol::this_state), void>::caller, sol::basic_object<sol::basic_reference > (&)(SomeClass&, sol::this_state)>(sol::types<sol::basic_object<sol::basic_reference >>, sol::types<SomeClass&, sol::this_state>, lua_State*, int, sol::wrapper<sol::basic_object<sol::basic_reference > ()(SomeClass&, sol::this_state), void>::caller&&, sol::basic_object<sol::basic_reference > (&)(SomeClass&, sol::this_state)) (tr=..., ta=..., L=0x7ffff7f9c380, start=1, fx=..., fxargs=@0x7fffffffcd20: 0x55555555c0e0 <main::$_0::__invoke(SomeClass&, sol::this_state)>) at /usr/include/sol/stack.hpp:281 #12 0x000055555558bec7 in sol::call_detail::agnostic_lua_call_wrapper<sol::basic_object<sol::basic_reference > ()(SomeClass&, sol::this_state), false, false, true, 0, true, void>::call<sol::basic_object<sol::basic_reference > (&)(SomeClass&, sol::this_state)> (L=0x7ffff7f9c380, f=@0x7fffffffcd20: 0x55555555c0e0 <main::$_0::_invoke(SomeClass&, sol::this_state)>) at /usr/include/sol/call.hpp:378 #13 0x000055555555c5bb in sol::call_detail::agnostic_lua_call_wrapper<main::$0, false, false, true, 0, true, void>::callmain::$_0& (L=0x7ffff7f9c380, f=...) at /usr/include/sol/call.hpp:371 #14 0x000055555555c54e in sol::call_detail::lua_call_wrapper<SomeClass, main::$0, false, false, true, 0, true, void>::callmain::$_0& (L=0x7ffff7f9c380, fx=...) at /usr/include/sol/call.hpp:602 #15 0x000055555555c4ee in sol::call_detail::call_wrapped<SomeClass, false, false, 0, true, true, main::$0&> (L=0x7ffff7f9c380, fx=...) at /usr/include/sol/call.hpp:911 #16 0x000055555555b87c in sol::u_detail::binding<char [6], main::$0, SomeClass>::call_with<false, false> (L=0x7ffff7f9c380, target=0x5555555ba728) at /usr/include/sol/usertype_storage.hpp:90 #17 0x000055555555c658 in sol::u_detail::binding<char [6], main::$0, SomeClass>::call<false, false> (L=0x7ffff7f9c380) at /usr/include/sol/usertype_storage.hpp:96 #18 0x000055555557537a in sol::detail::lua_cfunction_trampoline (L=0x7ffff7f9c380, f=0x55555555c600 <sol::u_detail::binding<char [6], main::$0, SomeClass>::call<false, false>(lua_State*)>) at /usr/include/sol/trampoline.hpp:105 #19 0x000055555555c69c in sol::detail::static_trampoline<&sol::u_detail::binding<char [6], main::$0, SomeClass>::call<false, false> > (L=0x7ffff7f9c380) at /usr/include/sol/trampoline.hpp:133 #20 0x000055555555c5f5 in typed_static_trampoline<int (*)(lua_State *), &sol::u_detail::binding<char[6], (lambda at poc.cpp:33:18), SomeClass>::call> (L=0x7ffff7f9c380) at /usr/include/sol/trampoline.hpp:201 #21 0x000055555555b965 in sol::u_detail::binding<char [6], main::$0, SomeClass>::call<false, false> (L=0x7ffff7f9c380) at /usr/include/sol/usertype_storage.hpp:101 #22 0x00007ffff7eebf06 in ?? () from /usr/lib/libluajit-5.1.so.2 #23 0x00007ffff7f02a90 in lua_pcall () from /usr/lib/libluajit-5.1.so.2 #24 0x0000555555567e8c in sol::basic_protected_function<sol::stack_reference, true, sol::basic_reference >::luacall (this=0x7fffffffd198, argcount=0, result_count=-1, h=...) at /usr/include/sol/protected_function.hpp:315 #25 0x0000555555567a37 in sol::basic_protected_function<sol::stack_reference, true, sol::basic_reference >::invoke(sol::types<>, std::integer_sequence, long, sol::detail::protected_handler<true, sol::basic_reference >&) const (this=0x7fffffffd198, n=0, h=...) at /usr/include/sol/protected_function.hpp:346 #26 0x00005555555677e1 in sol::basic_protected_function<sol::stack_reference, true, sol::basic_reference >::call<>() const (this=0x7fffffffd198) at /usr/include/sol/protected_function.hpp:254 #27 0x0000555555565b60 in sol::basic_protected_function<sol::stack_reference, true, sol::basic_reference >::operator()<>() const (this=0x7fffffffd198) at /usr/include/sol/protected_function.hpp:213 #28 0x0000555555565778 in sol::state_view::do_string (this=0x7fffffffd5e0, code="\n -- this fails properly\n local ok, err = pcall(function() tpl.say_hi() end)\n assert(not ok)\n\n -- and this crashes\n tpl.check() -- <---- mind the "." instead of ":"\n ", chunkname="", mode=sol::load_mode::any) at /usr/include/sol/state_view.hpp:339 #29 0x00005555555653fe in safe_script<sol::protected_function_result (&)(lua_State*, sol::protected_function_result), (sol::meta::enable_t)0> (this=0x7fffffffd5e0, code="\n -- this fails properly\n local ok, err = pcall(function() tpl.say_hi() end)\n assert(not ok)\n\n -- and this crashes\n tpl.check() -- <---- mind the "." instead of ":"\n ", on_error=@0x555555565500: {sol::protected_function_result (lua_State , sol::protected_function_result)} 0x555555565500 <sol::script_default_on_error(lua_State, sol::protected_function_result)>, chunkname="", mode=sol::load_mode::any) at /usr/include/sol/state_view.hpp:384 #30 0x000055555556538f in sol::state_view::safe_script (this=0x7fffffffd5e0, code="\n -- this fails properly\n local ok, err = pcall(function() tpl.say_hi() end)\n assert(not ok)\n\n -- and this crashes\n tpl.check() -- <---- mind the "." instead of ":"\n ", chunkname="", mode=sol::load_mode::any) at /usr/include/sol/state_view.hpp:409 #31 0x00005555555612b8 in sol::state_view::script (this=0x7fffffffd5e0, code="\n -- this fails properly\n local ok, err = pcall(function() tpl.say_hi() end)\n assert(not ok)\n\n -- and this crashes\n tpl.check() -- <---- mind the "." instead of ":"\n ", chunkname="", mode=sol::load_mode::any) at /usr/include/sol/state_view.hpp:562 #32 0x0000555555559880 in main (argc=1, argv=0x7fffffffd738) at poc.cpp:40

Additional notes

Linking against Lua 5.4 handles the invalid call gracefully:

Method is working as expected
[sol2] An error occurred and has been passed to an error handler: sol: runtime error: [string "..."]:3: stack index 1, expected userdata, received no value: value is not a valid userdata (bad argument into 'sol::basic_object<sol::basic_reference<false>>(SomeTemplate<int> &, sol::this_state)')
stack traceback:
	[C]: in field 'check'
	[string "..."]:3: in main chunk
terminate called after throwing an instance of 'sol::error'
  what():  sol: runtime error: [string "..."]:3: stack index 1, expected userdata, received no value: value is not a valid userdata (bad argument into 'sol::basic_object<sol::basic_reference<false>>(SomeTemplate<int> &, sol::this_state)')
stack traceback:
	[C]: in field 'check'
	[string "..."]:3: in main chunk
[1]    1178868 IOT instruction (core dumped)  ./main

naquad avatar Jul 08 '25 13:07 naquad

I think you need to load as a "safe_script" to have an exception instead of abort.

skhaz avatar Jul 08 '25 13:07 skhaz

safe_script is having SIGSEGV too.

naquad avatar Jul 08 '25 13:07 naquad

I'm also noticing a crash in get_no_lua_nil during value checking in certain circumstances when a bound C++ method is given e.g. a Lua table where a value-type userdata was expected. Using Luajit, there's a null-pointer segfault when pudata is dereferenced. Changing the expression there to pudata ? *pudata : nullptr just causes the code to crash elsewhere.

In this specific case I'm changing some code from using lua array-table values like [1,2] to a native vec2 type.

EvanBalster avatar Dec 05 '25 22:12 EvanBalster