Browse Source

[Pal/{Linux,Linux-SGX}] Block async signals during vfork

To avoid vfork child from corrupting parent's memory, block async
signal before vfork(). Children unblock async signal after execve in
{sgx_}signal_setup().
Isaku Yamahata 6 years ago
parent
commit
29fdd5fe1e

+ 18 - 29
Pal/src/host/Linux-SGX/sgx_exception.c

@@ -26,6 +26,7 @@
 #include "ecall_types.h"
 #include "ocall_types.h"
 #include "sgx_internal.h"
+#include "pal_linux.h"
 
 #include <atomic.h>
 #include <sigset.h>
@@ -35,11 +36,6 @@
 
 #include "sgx_enclave.h"
 
-#define IS_ERR INTERNAL_SYSCALL_ERROR
-#define IS_ERR_P INTERNAL_SYSCALL_ERROR_P
-#define ERRNO INTERNAL_SYSCALL_ERRNO
-#define ERRNO_P INTERNAL_SYSCALL_ERRNO_P
-
 #if !defined(__i386__)
 /* In x86_64 kernels, sigaction is required to have a user-defined
  * restorer. Also, they not yet support SA_INFO. The reference:
@@ -77,6 +73,15 @@ __attribute__((visibility("hidden"))) void __restore_rt(void);
 
 #endif
 
+static const int async_signals[] =
+{
+    SIGTERM,
+    SIGINT,
+    SIGCONT,
+};
+
+static const int nasync_signals = sizeof(async_signals) / sizeof(async_signals[0]);
+
 int set_sighandler (int * sigs, int nsig, void * handler)
 {
     struct sigaction action;
@@ -91,9 +96,8 @@ int set_sighandler (int * sigs, int nsig, void * handler)
     /* Disallow nested asynchronous signals during enclave exception handling.
      */
     __sigemptyset((__sigset_t *) &action.sa_mask);
-    __sigaddset((__sigset_t *) &action.sa_mask, SIGTERM);
-    __sigaddset((__sigset_t *) &action.sa_mask, SIGINT);
-    __sigaddset((__sigset_t *) &action.sa_mask, SIGCONT);
+    for (int i = 0; i < nasync_signals; i++)
+        __sigaddset((__sigset_t *) &action.sa_mask, async_signals[i]);
 
     for (int i = 0 ; i < nsig ; i++) {
         if (sigs[i] == SIGCHLD)
@@ -130,8 +134,9 @@ int set_sighandler (int * sigs, int nsig, void * handler)
     return 0;
 }
 
-int block_signals (int * sigs, int nsig)
+int block_signals (bool block, const int * sigs, int nsig)
 {
+    int how = block? SIG_BLOCK: SIG_UNBLOCK;
     int ret = 0;
     __sigset_t mask;
     __sigemptyset(&mask);
@@ -139,9 +144,9 @@ int block_signals (int * sigs, int nsig)
         __sigaddset(&mask, sigs[i]);
 
 #if defined(__i386__)
-    ret = INLINE_SYSCALL(sigprocmask, 3, SIG_BLOCK, &mask, NULL)
+    ret = INLINE_SYSCALL(sigprocmask, 3, how, &mask, NULL)
 #else
-    ret = INLINE_SYSCALL(rt_sigprocmask, 4, SIG_BLOCK, &mask, NULL,
+    ret = INLINE_SYSCALL(rt_sigprocmask, 4, how, &mask, NULL,
                          sizeof(sigset_t));
 #endif
 
@@ -151,25 +156,9 @@ int block_signals (int * sigs, int nsig)
     return 0;
 }
 
-int unblock_signals (int * sigs, int nsig)
+int block_async_signals (bool block)
 {
-    int ret = 0;
-    __sigset_t mask;
-    __sigemptyset(&mask);
-    for (int i = 0 ; i < nsig ; i++)
-        __sigaddset(&mask, sigs[i]);
-
-#if defined(__i386__)
-    ret = INLINE_SYSCALL(sigprocmask, 3, SIG_UNBLOCK, &mask, NULL)
-#else
-    ret = INLINE_SYSCALL(rt_sigprocmask, 4, SIG_UNBLOCK, &mask, NULL,
-                         sizeof(sigset_t));
-#endif
-
-    if (IS_ERR(ret))
-        return -ERRNO(ret);
-
-    return 0;
+    return block_signals(block, async_signals, nasync_signals);
 }
 
 int unset_sighandler (int * sigs, int nsig)

+ 2 - 0
Pal/src/host/Linux-SGX/sgx_internal.h

@@ -132,5 +132,7 @@ void sgx_edbgwr (void * addr, uint64_t data);
 
 int sgx_init_child_process (struct pal_sec * pal_sec);
 int sgx_signal_setup (void);
+int block_signals (bool block, const int * sigs, int nsig);
+int block_async_signals (bool block);
 
 #endif

+ 16 - 0
Pal/src/host/Linux-SGX/sgx_process.c

@@ -105,12 +105,28 @@ int sgx_create_process (const char * uri, int nargs, const char ** args,
     memcpy(argv + 1, args, sizeof(const char *) * nargs);
     argv[nargs + 1] = NULL;
 
+    /* Child's signal handler may mess with parent's memory during vfork(),
+     * so block signals
+     */
+    ret = block_async_signals(true);
+    if (ret < 0) {
+        ret = -ret;
+        goto out;
+    }
+
     ret = vfork_exec(proc_fds[0][0], proc_fds[1], argv);
     if (IS_ERR(ret))
         goto out;
 
     child = ret;
 
+    /* children unblock async signals by sgx_signal_setup() */
+    ret = block_async_signals(false);
+    if (ret < 0) {
+        ret = -ret;
+        goto out;
+    }
+
     for (int i = 0 ; i < 3 ; i++)
         INLINE_SYSCALL(close, 1, proc_fds[0][i]);
 

+ 52 - 0
Pal/src/host/Linux/db_exception.c

@@ -74,6 +74,15 @@ __attribute__((visibility("hidden"))) void __restore_rt(void);
 
 #endif
 
+static const int async_signals[] =
+{
+    SIGTERM,
+    SIGINT,
+    SIGCONT,
+};
+static const int nasync_signals = sizeof(async_signals) / sizeof(async_signals[0]);
+
+
 int set_sighandler (int * sigs, int nsig, void * handler)
 {
     struct sigaction action;
@@ -109,9 +118,52 @@ int set_sighandler (int * sigs, int nsig, void * handler)
             return -PAL_ERROR_DENIED;
     }
 
+    int ret = 0;
+    __sigset_t mask;
+    __sigemptyset(&mask);
+    for (int i = 0 ; i < nsig ; i++)
+        __sigaddset(&mask, sigs[i]);
+
+#if defined(__i386__)
+    ret = INLINE_SYSCALL(sigprocmask, 3, SIG_UNBLOCK, &mask, NULL)
+#else
+    ret = INLINE_SYSCALL(rt_sigprocmask, 4, SIG_UNBLOCK, &mask, NULL,
+                         sizeof(sigset_t));
+#endif
+
+    if (IS_ERR(ret))
+        return unix_to_pal_error(ERRNO(ret));
+
     return 0;
 }
 
+int block_signals (bool block, const int * sigs, int nsig)
+{
+    int how = block? SIG_BLOCK: SIG_UNBLOCK;
+    int ret = 0;
+    __sigset_t mask;
+    __sigemptyset(&mask);
+    for (int i = 0 ; i < nsig ; i++)
+        __sigaddset(&mask, sigs[i]);
+
+#if defined(__i386__)
+    ret = INLINE_SYSCALL(sigprocmask, 3, how, &mask, NULL)
+#else
+    ret = INLINE_SYSCALL(rt_sigprocmask, 4, how, &mask, NULL,
+                         sizeof(sigset_t));
+#endif
+
+    if (IS_ERR(ret))
+        return unix_to_pal_error(ERRNO(ret));
+
+    return 0;
+}
+
+int block_async_signals (bool block)
+{
+    return block_signals(block, async_signals, nasync_signals);
+}
+
 typedef struct {
     PAL_IDX         event_num;
     PAL_CONTEXT     context;

+ 12 - 0
Pal/src/host/Linux/db_process.c

@@ -299,6 +299,13 @@ int _DkProcessCreate (PAL_HANDLE * handle, const char * uri, const char ** args)
     proc_args->process_create_time = before_create;
 #endif
 
+    /* Child's signal handler may mess with parent's memory during vfork(),
+     * so block signals
+     */
+    ret = block_async_signals(true);
+    if (ret < 0)
+        goto out;
+
     ret = child_process(&param);
     if (IS_ERR(ret)) {
         ret = -PAL_ERROR_DENIED;
@@ -308,6 +315,11 @@ int _DkProcessCreate (PAL_HANDLE * handle, const char * uri, const char ** args)
     proc_args->pal_sec.process_id = ret;
     child_handle->process.pid = ret;
 
+    /* children unblock async signals by signal_setup() */
+    ret = block_async_signals(false);
+    if (ret < 0)
+        goto out;
+
     /* step 4: send parameters over the process handle */
 
     ret = INLINE_SYSCALL(write, 3,

+ 2 - 0
Pal/src/host/Linux/pal_linux.h

@@ -163,6 +163,8 @@ void init_child_process (PAL_HANDLE * parent, PAL_HANDLE * exec,
 
 void cpuid (unsigned int leaf, unsigned int subleaf,
             unsigned int words[]);
+int block_signals (bool block, const int * sigs, int nsig);
+int block_async_signals (bool block);
 void signal_setup (void);
 
 unsigned long _DkSystemTimeQueryEarly (void);