diff --git a/linux-user/qemu.h b/linux-user/qemu.h
index 989e01ad8d..54bf4f47be 100644
--- a/linux-user/qemu.h
+++ b/linux-user/qemu.h
@@ -177,6 +177,12 @@ typedef struct TaskState {
      * the table itself should be guarded.
      */
     struct fd_trans_table *fd_trans_tbl;
+
+    /*
+     * A table containing signal actions for the target. It should have at
+     * least TARGET_NSIG entries
+     */
+    struct target_sigaction *sigact_tbl;
 } __attribute__((aligned(16))) TaskState;
 
 extern char *exec_path;
@@ -419,7 +425,9 @@ void print_syscall_ret(int num, abi_long arg1);
  */
 void print_taken_signal(int target_signum, const target_siginfo_t *tinfo);
 
+
 /* signal.c */
+struct target_sigaction *sigact_table_clone(struct target_sigaction *orig);
 void process_pending_signals(CPUArchState *cpu_env);
 void signal_init(void);
 int queue_signal(CPUArchState *env, int sig, int si_type,
diff --git a/linux-user/signal.c b/linux-user/signal.c
index 8cf51ffecd..dc98def6d1 100644
--- a/linux-user/signal.c
+++ b/linux-user/signal.c
@@ -25,7 +25,13 @@
 #include "trace.h"
 #include "signal-common.h"
 
-static struct target_sigaction sigact_table[TARGET_NSIG];
+struct target_sigaltstack target_sigaltstack_used = {
+    .ss_sp = 0,
+    .ss_size = 0,
+    .ss_flags = TARGET_SS_DISABLE,
+};
+
+typedef struct target_sigaction sigact_table[TARGET_NSIG];
 
 static void host_signal_handler(int host_signum, siginfo_t *info,
                                 void *puc);
@@ -542,6 +548,11 @@ static void signal_table_init(void)
     }
 }
 
+struct target_sigaction *sigact_table_clone(struct target_sigaction *orig)
+{
+    return memcpy(g_new(sigact_table, 1), orig, sizeof(sigact_table));
+}
+
 void signal_init(void)
 {
     TaskState *ts = (TaskState *)thread_cpu->opaque;
@@ -556,6 +567,12 @@ void signal_init(void)
     /* Set the signal mask from the host mask. */
     sigprocmask(0, 0, &ts->signal_mask);
 
+    /*
+     * Set all host signal handlers. ALL signals are blocked during
+     * the handlers to serialize them.
+     */
+    ts->sigact_tbl = (struct target_sigaction *) g_new0(sigact_table, 1);
+
     sigfillset(&act.sa_mask);
     act.sa_flags = SA_SIGINFO;
     act.sa_sigaction = host_signal_handler;
@@ -568,9 +585,9 @@ void signal_init(void)
         host_sig = target_to_host_signal(i);
         sigaction(host_sig, NULL, &oact);
         if (oact.sa_sigaction == (void *)SIG_IGN) {
-            sigact_table[i - 1]._sa_handler = TARGET_SIG_IGN;
+            ts->sigact_tbl[i - 1]._sa_handler = TARGET_SIG_IGN;
         } else if (oact.sa_sigaction == (void *)SIG_DFL) {
-            sigact_table[i - 1]._sa_handler = TARGET_SIG_DFL;
+            ts->sigact_tbl[i - 1]._sa_handler = TARGET_SIG_DFL;
         }
         /* If there's already a handler installed then something has
            gone horribly wrong, so don't even try to handle that case.  */
@@ -608,11 +625,12 @@ void force_sig(int sig)
 #if !defined(TARGET_RISCV)
 void force_sigsegv(int oldsig)
 {
+    TaskState *ts = (TaskState *)thread_cpu->opaque;
     if (oldsig == SIGSEGV) {
         /* Make sure we don't try to deliver the signal again; this will
          * end up with handle_pending_signal() calling dump_core_and_abort().
          */
-        sigact_table[oldsig - 1]._sa_handler = TARGET_SIG_DFL;
+        ts->sigact_tbl[oldsig - 1]._sa_handler = TARGET_SIG_DFL;
     }
     force_sig(TARGET_SIGSEGV);
 }
@@ -837,6 +855,7 @@ int do_sigaction(int sig, const struct target_sigaction *act,
     struct sigaction act1;
     int host_sig;
     int ret = 0;
+    TaskState* ts = (TaskState *)thread_cpu->opaque;
 
     trace_signal_do_sigaction_guest(sig, TARGET_NSIG);
 
@@ -848,7 +867,7 @@ int do_sigaction(int sig, const struct target_sigaction *act,
         return -TARGET_ERESTARTSYS;
     }
 
-    k = &sigact_table[sig - 1];
+    k = &ts->sigact_tbl[sig - 1];
     if (oact) {
         __put_user(k->_sa_handler, &oact->_sa_handler);
         __put_user(k->sa_flags, &oact->sa_flags);
@@ -930,7 +949,7 @@ static void handle_pending_signal(CPUArchState *cpu_env, int sig,
         sa = NULL;
         handler = TARGET_SIG_IGN;
     } else {
-        sa = &sigact_table[sig - 1];
+        sa = &ts->sigact_tbl[sig - 1];
         handler = sa->_sa_handler;
     }
 
@@ -1022,9 +1041,9 @@ void process_pending_signals(CPUArchState *cpu_env)
              * looping round and round indefinitely.
              */
             if (sigismember(&ts->signal_mask, target_to_host_signal_table[sig])
-                || sigact_table[sig - 1]._sa_handler == TARGET_SIG_IGN) {
+                || ts->sigact_tbl[sig - 1]._sa_handler == TARGET_SIG_IGN) {
                 sigdelset(&ts->signal_mask, target_to_host_signal_table[sig]);
-                sigact_table[sig - 1]._sa_handler = TARGET_SIG_DFL;
+                ts->sigact_tbl[sig - 1]._sa_handler = TARGET_SIG_DFL;
             }
 
             handle_pending_signal(cpu_env, sig, &ts->sync_signal);
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index ff1d07871f..838caf9c98 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -5989,6 +5989,17 @@ static int do_fork(CPUArchState *env, unsigned int flags, abi_ulong newsp,
         return -TARGET_EINVAL;
     }
 
+    if ((flags & CLONE_SIGHAND) && !(flags & CLONE_VM)) {
+        /*
+         * Like CLONE_FILES, this flag combination is unsupported. If
+         * CLONE_SIGHAND is specified without CLONE_VM, then we need to keep
+         * the sigact table in-sync across virtual memory boundaries, which is
+         * substantially more complicated.
+         */
+        qemu_log_mask(LOG_UNIMP, "CLONE_SIGHAND only supported with CLONE_VM");
+        return -TARGET_EINVAL;
+    }
+
     pthread_mutex_init(&info.mutex, NULL);
     pthread_mutex_lock(&info.mutex);
     pthread_cond_init(&info.cond, NULL);
@@ -6025,6 +6036,12 @@ static int do_fork(CPUArchState *env, unsigned int flags, abi_ulong newsp,
         ts->fd_trans_tbl = fd_trans_table_clone(parent_ts->fd_trans_tbl);
     }
 
+    if (flags & CLONE_SIGHAND) {
+        ts->sigact_tbl = parent_ts->sigact_tbl;
+    } else {
+        ts->sigact_tbl = sigact_table_clone(parent_ts->sigact_tbl);
+    }
+
     if (flags & CLONE_CHILD_CLEARTID) {
         ts->child_tidptr = child_tidptr;
     }
