in membarrier fallback, allow for possibility that sigaction fails
authorRich Felker <dalias@aerifal.cx>
Tue, 9 Apr 2019 21:51:54 +0000 (17:51 -0400)
committerRich Felker <dalias@aerifal.cx>
Tue, 9 Apr 2019 21:51:54 +0000 (17:51 -0400)
this is a workaround to avoid a crashing regression on qemu-user when
dynamic TLS is installed at dlopen time. the sigaction syscall should
not be able to fail, but it does fail for implementation-internal
signals under qemu user-level emulation if the host libc qemu is
running under reserves the same signals for implementation-internal
use, since qemu makes no provision to redirect/emulate them. after
sigaction fails, the subsequent tkill would terminate the process
abnormally as the default action.

no provision to account for membarrier failing is made in the dynamic
linker code that installs new TLS. at the formal level, the missing
barrier in this case is incorrect, and perhaps we should fail the
dlopen operation, but in practice all the archs we support (and
probably all real-world archs except alpha, which isn't yet supported)
should give the right behavior with no barrier at all as a consequence
of consume-order properties.

in the long term, this workaround should be supplemented or replaced
by something better -- a different fallback approach to ensuring
memory consistency, or dynamic allocation of implementation-internal
signals. the latter is appealing in that it would allow cancellation
to work under qemu-user too, and would even allow many levels of
nested emulation.

src/linux/membarrier.c

index 26d143e79cb026610183d99c26e27eb7fa6aaca8..9ebe906ed8b25d28590e2b909006969f4beee0c0 100644 (file)
@@ -44,17 +44,18 @@ int __membarrier(int cmd, int flags)
                        .sa_handler = bcast_barrier
                };
                memset(&sa.sa_mask, -1, sizeof sa.sa_mask);
-               __libc_sigaction(SIGSYNCCALL, &sa, 0);  
-               for (td=self->next; td!=self; td=td->next)
-                       __syscall(SYS_tkill, td->tid, SIGSYNCCALL);
-               for (td=self->next; td!=self; td=td->next)
-                       sem_wait(&barrier_sem);
-               sa.sa_handler = SIG_IGN;
-               __libc_sigaction(SIGSYNCCALL, &sa, 0);
+               if (!__libc_sigaction(SIGSYNCCALL, &sa, 0)) {
+                       for (td=self->next; td!=self; td=td->next)
+                               __syscall(SYS_tkill, td->tid, SIGSYNCCALL);
+                       for (td=self->next; td!=self; td=td->next)
+                               sem_wait(&barrier_sem);
+                       r = 0;
+                       sa.sa_handler = SIG_IGN;
+                       __libc_sigaction(SIGSYNCCALL, &sa, 0);
+               }
                sem_destroy(&barrier_sem);
                __tl_unlock();
                __restore_sigs(&set);
-               return 0;
        }
        return __syscall_ret(r);
 }