Print this page
OS-4470 lxbrand unblocking signals in new threads must be atomic
Reviewed by: Robert Mustacchi <rm@joyent.com>
OS-3561 lxbrand emulation library should execute on alternate stack
OS-3558 lxbrand add support for full in-kernel syscall handling
OS-3545 lx_syscall_regs should not walk stack
OS-3868 many LTP testcases now hang
OS-3901 lxbrand lx_recvmsg fails to translate control messages when 64-bit
Reviewed by: Jerry Jelinek <jerry.jelinek@joyent.com>
Reviewed by: Patrick Mooney <patrick.mooney@joyent.com>
Reviewed by: Bryan Cantrill <bryan@joyent.com>
OS-2844 lx brand should support 64-bit user-land

@@ -20,10 +20,11 @@
  */
 
 /*
  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ * Copyright 2015 Joyent, Inc.
  */
 
 #include "lint.h"
 #include <sys/feature_tests.h>
 /*

@@ -282,11 +283,29 @@
                 continue;
         if (error)
                 thr_panic("take_deferred_signal(): __sigresend() failed");
 }
 
+/*
+ * sigacthandler() attempts to clean up dangling uc_link pointers in
+ * signal handling contexts when libc believes us to have escaped
+ * a signal handler incorrectly in the past.
+ *
+ * Branded processes have a legitimate use for a chain including contexts
+ * other than those used for signal handling when tracking emulation
+ * requests from the kernel.  We allow them to disable this cleanup
+ * behaviour.
+ */
+static int escaped_context_cleanup = 1;
+
 void
+set_escaped_context_cleanup(int on)
+{
+        escaped_context_cleanup = on;
+}
+
+void
 sigacthandler(int sig, siginfo_t *sip, void *uvp)
 {
         ucontext_t *ucp = uvp;
         ulwp_t *self = curthread;
 

@@ -304,11 +323,11 @@
          * thinking that it is still in the signal context.  We repair this
          * possible damage by setting ucp->uc_link to NULL if we know that
          * we are actually executing at main level (self->ul_siglink == NULL).
          * See the code for setjmp()/longjmp() for more details.
          */
-        if (self->ul_siglink == NULL)
+        if (escaped_context_cleanup && self->ul_siglink == NULL)
                 ucp->uc_link = NULL;
 
         /*
          * If we are not in a critical region and are
          * not deferring signals, take the signal now.

@@ -456,23 +475,27 @@
                 lrw_unlock(&udp->siguaction[sig].sig_lock);
         return (rv);
 }
 
 /*
- * This is a private interface for the linux brand interface.
+ * This is a private interface for the lx brand.
  */
 void
 setsigacthandler(void (*nsigacthandler)(int, siginfo_t *, void *),
-    void (**osigacthandler)(int, siginfo_t *, void *))
+    void (**osigacthandler)(int, siginfo_t *, void *),
+    int (*brsetctxt)(const ucontext_t *))
 {
         ulwp_t *self = curthread;
         uberdata_t *udp = self->ul_uberdata;
 
         if (osigacthandler != NULL)
                 *osigacthandler = udp->sigacthandler;
 
         udp->sigacthandler = nsigacthandler;
+
+        if (brsetctxt != NULL)
+                udp->setctxt = brsetctxt;
 }
 
 /*
  * Tell the kernel to block all signals.
  * Use the schedctl interface, or failing that, use __lwp_sigmask().

@@ -515,15 +538,43 @@
 set_setcontext_enforcement(int on)
 {
         setcontext_enforcement = on;
 }
 
+/*
+ * The LX brand emulation library implements an operation that is analogous to
+ * setcontext(), but takes a different path in to the kernel.  So that it can
+ * correctly restore a signal mask, we expose just the signal mask handling
+ * part of the regular setcontext() routine as a private interface.
+ */
+void
+setcontext_sigmask(ucontext_t *ucp)
+{
+        ulwp_t *self = curthread;
+
+        if (ucp->uc_flags & UC_SIGMASK) {
+                block_all_signals(self);
+                delete_reserved_signals(&ucp->uc_sigmask);
+                self->ul_sigmask = ucp->uc_sigmask;
+                if (self->ul_cursig) {
+                        /*
+                         * We have a deferred signal present.
+                         * The signal mask will be set when the
+                         * signal is taken in take_deferred_signal().
+                         */
+                        ASSERT(self->ul_critical + self->ul_sigdefer != 0);
+                        ucp->uc_flags &= ~UC_SIGMASK;
+                }
+        }
+}
+
 #pragma weak _setcontext = setcontext
 int
 setcontext(const ucontext_t *ucp)
 {
         ulwp_t *self = curthread;
+        uberdata_t *udp = self->ul_uberdata;
         int ret;
         ucontext_t uc;
 
         /*
          * Returning from the main context (uc_link == NULL) causes

@@ -534,24 +585,11 @@
         (void) memcpy(&uc, ucp, sizeof (uc));
 
         /*
          * Restore previous signal mask and context link.
          */
-        if (uc.uc_flags & UC_SIGMASK) {
-                block_all_signals(self);
-                delete_reserved_signals(&uc.uc_sigmask);
-                self->ul_sigmask = uc.uc_sigmask;
-                if (self->ul_cursig) {
-                        /*
-                         * We have a deferred signal present.
-                         * The signal mask will be set when the
-                         * signal is taken in take_deferred_signal().
-                         */
-                        ASSERT(self->ul_critical + self->ul_sigdefer != 0);
-                        uc.uc_flags &= ~UC_SIGMASK;
-                }
-        }
+        setcontext_sigmask(&uc);
         self->ul_siglink = uc.uc_link;
 
         /*
          * We don't know where this context structure has been.
          * Preserve the curthread pointer, at least.

@@ -576,11 +614,11 @@
          * or ___lwp_cond_wait() that it returns right away
          * (giving us a spurious wakeup but not a deadlock).
          */
         set_parking_flag(self, 0);
         self->ul_sp = 0;
-        ret = __setcontext(&uc);
+        ret = udp->setctxt(&uc);
 
         /*
          * It is OK for setcontext() to return if the user has not specified
          * UC_CPU.
          */