On Thu, May 06, 2021 at 09:32:28AM +0200, Sebastien Marie wrote:
> Hi,
> 
> Anindya, did a good analysis of the problem with mpv using gpu video
> output backend (it is using EGL and mesa if I correctly followed).
> 
> 
> For people not reading ports@ here a resume: the destructor function
> used in pthread_key_create() needs to be present in memory until
> _rthread_tls_destructors() is called.
> 
> in the case of mesa, eglInitialize() function could load, via
> dlopen(), code which will use pthread_key_create() with destructor.
> 
> once dlclose() is called, the object is unloaded from memory, but a
> reference to destructor is kept, leading to segfault when
> _rthread_tls_destructors() run and use the destructor (because
> pointing to unloaded code).
>

I was going deeper in the analysis.

At first, I tought that the pthread_key_create() call was going from
mesa driver (radeonsi_dri.so on my machine) as pinning the DSO in
memory (using LD_PRELOAD) permitted to avoid the segfault.

In fact, it isn't directly radeonsi_dri.so but another dependant
library: libLLVM.so.5.0 in this case (by using
LD_PRELOAD=.../libLLVM.so.5.0, the crash disapparear).

Searching where is located the pthread_key_create() call, I found that
it was coming from emutls implementation (which is using
pthread_key_create + destructor) and which is statically linked with
compiler-rt.a

By instrumenting pthread_key_create, I have the following backtrace
(the abort(3) is mine):

(gdb) bt
#0  thrkill () at /tmp/-:3
#1  0x000005188f550abe in _libc_abort () at /usr/src/lib/libc/stdlib/abort.c:51
#2  0x000005191c7e8c2b in pthread_key_create () from 
/home/semarie/Documents/devel/libhijacking/libthread.so
#3  0x00000519399e6a87 in emutls_init () at 
/usr/src/gnu/lib/libcompiler_rt/../../llvm/compiler-rt/lib/builtins/emutls.c:118
#4  0x000005188f55b4f7 in pthread_once (once_control=0x51939d00b30 
<emutls_init_once.once>, init_routine=0x27240efb23d627ef) at 
/usr/src/lib/libc/thread/rthread_once.c:26
#5  0x00000519399e68dd in emutls_init_once () at 
/usr/src/gnu/lib/libcompiler_rt/../../llvm/compiler-rt/lib/builtins/emutls.c:125
#6  emutls_get_index (control=0x51939cae5c8 
<__emutls_v._ZL25TimeTraceProfilerInstance>) at 
/usr/src/gnu/lib/libcompiler_rt/../../llvm/compiler-rt/lib/builtins/emutls.c:316
#7  __emutls_get_address (control=0x51939cae5c8 
<__emutls_v._ZL25TimeTraceProfilerInstance>) at 
/usr/src/gnu/lib/libcompiler_rt/../../llvm/compiler-rt/lib/builtins/emutls.c:379
#8  0x00000519387f296e in llvm::getTimeTraceProfilerInstance() () from 
/usr/lib/libLLVM.so.5.0
#9  0x0000051938ec2bf2 in llvm::legacy::PassManagerImpl::run(llvm::Module&) () 
from /usr/lib/libLLVM.so.5.0
#10 0x000005193974eb67 in LLVMRunPassManager () from /usr/lib/libLLVM.so.5.0
#11 0x00000518d11276d8 in ?? () from /usr/X11R6/lib/modules/dri/radeonsi_dri.so
#12 0x00000518d1082761 in ?? () from /usr/X11R6/lib/modules/dri/radeonsi_dri.so
#13 0x00000518d110b1ea in ?? () from /usr/X11R6/lib/modules/dri/radeonsi_dri.so
#14 0x00000518d0c7939c in ?? () from /usr/X11R6/lib/modules/dri/radeonsi_dri.so
#15 0x00000518d0c794ed in ?? () from /usr/X11R6/lib/modules/dri/radeonsi_dri.so
#16 0x00000518d1cfbec1 in _rthread_start (v=<error reading variable: Unhandled 
dwarf expression opcode 0xa3>) at /usr/src/lib/librthread/rthread.c:96
#17 0x000005188f52bd2a in __tfork_thread () at 
/usr/src/lib/libc/arch/amd64/sys/tfork_thread.S:84

It means that emutls implementation we are using couldn't be safely
used if the code is using dlopen(3).


I made the following PoC using __thread :

$ cat lib.c
#include <stdio.h>

__thread int value = 0;

void
fn()
{
        printf("entering:  %s\n", __func__);
        value = 1;
        printf("returning: %s\n", __func__);
}

$ cat main.c
#include <err.h>
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

void *
loadcode(void *arg)
{
        void *lib;
        void (*fn)();

        printf("thread: entering\n");
        
        printf("dlopen(3)\n");
        if ((lib = dlopen("./lib.so", 0)) == NULL)
                errx(EXIT_FAILURE, "dlopen: %s", dlerror());
        
        if ((fn = dlsym(lib, "fn")) == NULL)
                errx(EXIT_FAILURE, "dlsym: %s", dlerror());

        fn();

        printf("dlclose(3)\n");
        if (dlclose(lib) != 0)
                errx(EXIT_FAILURE, "dlclose: %s", dlerror());

        printf("thread: returning\n");
        return arg;
}

int
main(int argc, char *argv[])
{
        int error;
        pthread_t th;

        if ((error = pthread_create(&th, NULL, &loadcode, NULL)) != 0)
                errc(error, EXIT_FAILURE, "pthread_create");
        
        if ((error = pthread_join(th, NULL)) != 0)
                errc(error, EXIT_FAILURE, "pthread_join");
                
        return EXIT_SUCCESS;
}

$ cc lib.c -Wall -lpthread -shared -fPIC -o lib.so
$ cc main.c -Wall -lpthread
$ ./a.out
thread: entering
dlopen(3)
entering:  fn
returning: fn
dlclose(3)
thread: returning
Segmentation fault (core dumped) 

(gdb) bt
#0  0x000004eb3011aec0 in ?? ()
#1  0x000004eb7fc41b75 in _rthread_tls_destructors (thread=0x4eaccaa5c40) at 
/usr/src/lib/libc/thread/rthread_tls.c:182
#2  0x000004eb7fbd9cd3 in _libc_pthread_exit (retval=<error reading variable: 
Unhandled dwarf expression opcode 0xa3>) at 
/usr/src/lib/libc/thread/rthread.c:150
#3  0x000004eb06814ec9 in _rthread_start (v=<error reading variable: Unhandled 
dwarf expression opcode 0xa3>) at /usr/src/lib/librthread/rthread.c:97
#4  0x000004eb7fbedd2a in __tfork_thread () at 
/usr/src/lib/libc/arch/amd64/sys/tfork_thread.S:84

-- 
Sebastien Marie

Reply via email to