Follow @Openwall on Twitter for new release announcements and other news
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date: Sun, 22 Nov 2020 14:11:07 -0500
From: Rich Felker <dalias@...c.org>
To: Арсений <a@...r0n.science>
Cc: musl@...ts.openwall.com
Subject: Re: Mutexes are not unlocking

On Sun, Nov 22, 2020 at 09:43:35PM +0300, Арсений wrote:
> 
> Hello,
> The problem is that mutex is not got unlocked after the first unlock().
>  
> libstdc++ uses a wrapper for pthread called gthreads. This wrapper
> checks for the state of the mutex system. For
> example, pthread_mutex_unlock() is called in a following way:
>  
> static inline int
> __gthread_mutex_unlock (__gthread_mutex_t *__mutex)
> {
>   if (__gthread_active_p ())
>     return __gthrw_(pthread_mutex_unlock) (__mutex);
>   else
>     return 0;
> }

Yes. This code is invalid (it misinterprets weak symbol information to
draw incorrect conclusions about whether threads may be in use) and
thus is disabled in builds of gcc/libstdc++ targeting musl-based
systems. GCC and glibc-based distro folks mostly don't care because
it only breaks static linking, but some of them actually hack gcc's
libpthread.a into one giant .o file to work around the problem rather
than fixing this in gcc...

> The function __gthread_active_p() is an inline function which
> returns non-nullptr value is pthreads is available.
> 
> It seems that std::mutex::lock() in libstdc++ from Alpine Linux
> repos does not use ghtreads:

Yes. That's a standard requirement for building a musl-targeting gcc.

>  
> => 0x00007ffff7eb6dde <+0>:     push   %rdx
>    0x00007ffff7eb6ddf <+1>:     callq  0x7ffff7eaf550 <pthread_mutex_lock@plt>
>    0x00007ffff7eb6de4 <+6>:     test   %eax,%eax
>    0x00007ffff7eb6de6 <+8>:     je     0x7ffff7eb6def <_ZNSt5mutex4lockEv+17>
>    0x00007ffff7eb6de8 <+10>:    mov    %eax,%edi
>    0x00007ffff7eb6dea <+12>:    callq  0x7ffff7eb3f50 <_ZSt20__throw_system_errori@plt>
>    0x00007ffff7eb6def <+17>:    pop    %rax
>    0x00007ffff7eb6df0 <+18>:    retq 
>  
> std::mutex::lock() from glibc-compatible libstdc++ is not inlined by
> compiler during build, because it contains check for errors:
>     void
>     lock()
>     {
>       int __e = __gthread_mutex_lock(&_M_mutex);
> 
>       // EINVAL, EAGAIN, EBUSY, EINVAL, EDEADLK(may)
>       if (__e)
>     __throw_system_error(__e);
>     }
>  
> std::mutex::unlock() from glibc-compatible libstdc++ is inlined,
> because it does not contain anything but call to
> __gthread_mutex_lock():
>     void
>     unlock()
>     {
>       // XXX EINVAL, EAGAIN, EPERM
>       __gthread_mutex_unlock(&_M_mutex);
>     }
> This is why pthread_mutex_lock() from musl is called, but pthread_mutex_unlock() is not.

Thanks for doing the root cause analysis here. It will be interesting
(and probably infuriating) to the gcompat folks working on making
glibc-linked binaries work on musl, but maybe they already have a
workaround for this.

Anyway the answer to your original question is probably just "don't do
that" unless you want to get into fixing GCC...

Rich

Powered by blists - more mailing lists

Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.