Sol2: userdata&type segvs at 0x0 when binary is LD_PRELOADed

Created on 4 Oct 2016  Â·  15Comments  Â·  Source: ThePhD/sol2

Usecase: I'm trying to embed lua in a existing 32bit binary that I don't have source code for. To that end, I'm compiling lua (and sol) into a shared library, that I then insert with LD_PRELOAD.

This works fine; however, when attempting to register a usertype, or even just assign a pointer to a userdata (class), the whole thing segfaults.

I've tested against luajit-2.0 and lua-5.1. sol2 2.14.9.

Needless to say, the same code works fine when running "standalone".

What am I doing wrong?! :confused:

Edit: the same happens with all but the most trivial anonymous functions - anything that references or binds outside the scope, I guess.

yay.c

#include <stdio.h>

int main()
{
    printf("yay!\n");
}

Test.cpp

#include "lib/sol.hpp"

struct Test {
    Test()
    {
        sol::state lua;
        lua.open_libraries(sol::lib::base);

        printf("bye!\n");
        // fails too: lua.new_usertype<Test>("test");
        lua["test"] = this;
        printf("wow?\n");
    }
    int test() { return 5;  }
};

Test preload;
$ gcc -m32 yay.c -o yay
$ ./yay
yay!
$ g++ -m32 -fPIC -shared  -I/usr/include/lua5.1 -std=c++14 Test.cpp -o Test /usr/lib/i386-linux-gnu/liblua5.1.a -ldl
$ LD_PRELOAD=/usr/lib/i386-linux-gnu/libasan.so.2:./Test ./yay
bye!
ASAN:SIGSEGV
=================================================================
==92152==ERROR: AddressSanitizer: SEGV on unknown address 0x00000000 (pc 0xf705f3c6 bp 0xffb8f858 sp 0xffb8f3e4 T0)
    #0 0xf705f3c5  (/lib/i386-linux-gnu/libc.so.6+0x7e3c5)
    #1 0xf724f655 in __interceptor_strlen (/usr/lib/i386-linux-gnu/libasan.so.2+0x6a655)
    #2 0xf71b87bf in lua_getfield (Test+0x117bf)
    #3 0xf71c4e38 in luaL_newmetatable (Test+0x1de38)
    #4 0xf71b7158 in int sol::stack::pusher<sol::detail::as_pointer_tag<Test>, void>::push_keyed<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&>(lua_State*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, Test*)::{lambda()#1}::operator()() const (Test+0x10158)
    #5 0xf71b7284 in int sol::stack::pusher<sol::detail::as_pointer_tag<Test>, void>::push_fx<int sol::stack::pusher<sol::detail::as_pointer_tag<Test>, void>::push_keyed<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&>(lua_State*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, Test*)::{lambda()#1}>(lua_State*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, Test*) (Test+0x10284)
    #6 0xf71b71c6 in int sol::stack::pusher<sol::detail::as_pointer_tag<Test>, void>::push_keyed<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&>(lua_State*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, Test*) (Test+0x101c6)
    #7 0xf71b7029 in sol::stack::pusher<sol::detail::as_pointer_tag<Test>, void>::push(lua_State*, Test*) (Test+0x10029)
    #8 0xf71b6ff9 in int sol::stack::pusher<Test*, void>::push<Test*>(lua_State*, Test*&&) (Test+0xfff9)
    #9 0xf71b6fc3 in int sol::stack::push<Test*>(lua_State*, Test*&&) (Test+0xffc3)
    #10 0xf71b6f77 in void sol::stack::field_setter<char [5], true, false, void>::set<char const (&) [5], Test*>(lua_State*, char const (&) [5], Test*&&, int) (Test+0xff77)
    #11 0xf71b6f2e in void sol::stack::set_field<true, false, char const (&) [5], Test*>(lua_State*, char const (&) [5], Test*&&) (Test+0xff2e)
    #12 0xf71b6eaf in void sol::basic_table_core<true, sol::reference>::traverse_set_deep<true, char const (&) [5], Test*>(char const (&) [5], Test*&&) const (Test+0xfeaf)
    #13 0xf71b6d95 in sol::basic_table_core<true, sol::reference>& sol::basic_table_core<true, sol::reference>::traverse_set<char const (&) [5], Test*>(char const (&) [5], Test*&&) (Test+0xfd95)
    #14 0xf71b6c83 in void sol::proxy<sol::basic_table_core<true, sol::reference>&, char const (&) [5]>::tuple_set<0u, Test*>(std::integer_sequence<unsigned int, 0u>, Test*&&) const (Test+0xfc83)
    #15 0xf71b6ade in sol::proxy<sol::basic_table_core<true, sol::reference>&, char const (&) [5]>& sol::proxy<sol::basic_table_core<true, sol::reference>&, char const (&) [5]>::set<Test*>(Test*&&) (Test+0xfade)
    #16 0xf71b6985 in sol::proxy<sol::basic_table_core<true, sol::reference>&, char const (&) [5]>& sol::proxy<sol::basic_table_core<true, sol::reference>&, char const (&) [5]>::operator=<Test*, (sol::meta::enable_t)0>(Test*&&) (Test+0xf985)
    #17 0xf71b5ee8 in Test::Test() (Test+0xeee8)
    #18 0xf71b505c in __static_initialization_and_destruction_0(int, int) (Test+0xe05c)
    #19 0xf71b53ec in _GLOBAL__sub_I_Test.cpp (Test+0xe3ec)
    #20 0xf7758364  (/lib/ld-linux.so.2+0xf364)
    #21 0xf775848d  (/lib/ld-linux.so.2+0xf48d)
    #22 0xf7749afe  (/lib/ld-linux.so.2+0xafe)

AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV ??:0 ??
==92152==ABORTING
What.In The World

Most helpful comment

This also explains why the bug fled the scene in later iterations: static initialization order is finicky, and can depend on many things and changes between compiler versions, so... it could disappear in the blink of an eye. A true Heisenbug.

All 15 comments

I... don't know where to begin. This is a niche use case, for sure. It's going to take me some time to figure it out. It's a good thing I just got my VM spun up, here...

Mmm, can you repro it with the testcase I posted? If it's any help, it's a Ubuntu 16.04 LTS VM on my end.

I've been hacking away at it all day (on my big fat codebase that does a LOT more stuff) and now it .. kind of .. works, but I'm completely cluesless as to why.

Give me a few days. This is complicated and I have a team project meeting in two days and a midterm tomorrow. :V

No hurries. Thanks for looking into it! I realise this is a cornercase from cornerland, but since lua's purpose is to be embedded in all kinds of crazy environs .. :)

Best of luck with your midterm thing!

Its probably something crazy luajit does to allocate executable memory in a fixed area or something, possible to try with "normal" lua?

Hey,

as you can see from the text, I've tried with both luajit2.0 and lua5.1 and both exhibit the exact same crashiness. :)

Ah, its just luajit needed some magic imagebase to be set to work on OSX - just checking it wasn't that.

FWIW, my current code works. I'm not doing anything really different as far as I can tell, but obviously there must be something. If I ever figure why the hell that is, I'll update this issue.

Probably global static stuff is something to do with it __static_initialization_and_destruction_0

I can't figure this bug out at all. Do you have any idea what you changed? Whether I compile for -m32 or not, the code always segfaults horrifically.

Reading about uses of LD_PRELOAD, this looks to be about loading a DLL that overrides some functionality. I'm going to guess LD_PRELOAD isn't... actually overriding anything, and there's a global static initializer which some places on the web say isn't exactly a good idea to begin with...

Removing the global static "Test" will probably make it go away, I'd give that a go. You should probably create/init off a dedicated function called from the entry point. (so in this case create it in main).

There's DLL main on Windows. No idea how Linux plays ball...

On Oct 15, 2016 6:03 AM, "Paul" [email protected] wrote:

Removing the global static "Test" will probably make it go away, I'd give
that a go. You should probably create/init off a dedicated function called
from the entry point.

—
You are receiving this because you were assigned.
Reply to this email directly, view it on GitHub
https://github.com/ThePhD/sol2/issues/240#issuecomment-253983075, or mute
the thread
https://github.com/notifications/unsubscribe-auth/ACwLJP0CLDi1WkPMyKv_ga6jXg9ksafGks5q0M89gaJpZM4KNlIp
.

Great news:

yay, indeed~

This should be fixed now. The bug was indeed related to a global static initializers: usertype_traits's std::strings weren't initialized in time for usage with your code, so everything crapped itself.

Pull from latest, or get: https://github.com/ThePhD/sol2/releases/tag/v2.14.12

Thanks to @Naav for tracking this one down and telling me what was actually going on. I don't know how they got deep enough to, but they did! I'll have to ask them about their debug techniques sometime...

This also explains why the bug fled the scene in later iterations: static initialization order is finicky, and can depend on many things and changes between compiler versions, so... it could disappear in the blink of an eye. A true Heisenbug.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

shohnwal picture shohnwal  Â·  4Comments

morsisko picture morsisko  Â·  3Comments

karenzshea picture karenzshea  Â·  4Comments

squeek502 picture squeek502  Â·  3Comments

cgloeckner picture cgloeckner  Â·  3Comments