This is pre-work for adding full support for the `CLONE_VM` `clone`
flag. In a follow-up patch, we'll add support to `clone.c` for
`clone_vm`-type clones beyond threads. CLONE_VM support is more
complicated, so first we're splitting existing clone mechanisms
(pthread_create, and fork) into a separate file.

Signed-off-by: Josh Kunz <j...@google.com>
---
 linux-user/Makefile.objs |   2 +-
 linux-user/clone.c       | 152 ++++++++++++++++
 linux-user/clone.h       |  27 +++
 linux-user/syscall.c     | 376 +++++++++++++++++++--------------------
 4 files changed, 365 insertions(+), 192 deletions(-)
 create mode 100644 linux-user/clone.c
 create mode 100644 linux-user/clone.h

diff --git a/linux-user/Makefile.objs b/linux-user/Makefile.objs
index 1940910a73..d6788f012c 100644
--- a/linux-user/Makefile.objs
+++ b/linux-user/Makefile.objs
@@ -1,7 +1,7 @@
 obj-y = main.o syscall.o strace.o mmap.o signal.o \
        elfload.o linuxload.o uaccess.o uname.o \
        safe-syscall.o $(TARGET_ABI_DIR)/signal.o \
-        $(TARGET_ABI_DIR)/cpu_loop.o exit.o fd-trans.o
+        $(TARGET_ABI_DIR)/cpu_loop.o exit.o fd-trans.o clone.o
 
 obj-$(TARGET_HAS_BFLT) += flatload.o
 obj-$(TARGET_I386) += vm86.o
diff --git a/linux-user/clone.c b/linux-user/clone.c
new file mode 100644
index 0000000000..f02ae8c464
--- /dev/null
+++ b/linux-user/clone.c
@@ -0,0 +1,152 @@
+#include "qemu/osdep.h"
+#include "qemu.h"
+#include "clone.h"
+#include <pthread.h>
+#include <sched.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <stdbool.h>
+#include <assert.h>
+
+static const unsigned long NEW_STACK_SIZE = 0x40000UL;
+
+/*
+ * A completion tracks an event that can be completed. It's based on the
+ * kernel concept with the same name, but implemented with userspace locks.
+ */
+struct completion {
+    /* done is set once this completion has been completed. */
+    bool done;
+    /* mu syncronizes access to this completion. */
+    pthread_mutex_t mu;
+    /* cond is used to broadcast completion status to awaiting threads. */
+    pthread_cond_t cond;
+};
+
+static void completion_init(struct completion *c)
+{
+    c->done = false;
+    pthread_mutex_init(&c->mu, NULL);
+    pthread_cond_init(&c->cond, NULL);
+}
+
+/*
+ * Block until the given completion finishes. Returns immediately if the
+ * completion has already finished.
+ */
+static void completion_await(struct completion *c)
+{
+    pthread_mutex_lock(&c->mu);
+    if (c->done) {
+        pthread_mutex_unlock(&c->mu);
+        return;
+    }
+    pthread_cond_wait(&c->cond, &c->mu);
+    assert(c->done && "returned from cond wait without being marked as done");
+    pthread_mutex_unlock(&c->mu);
+}
+
+/*
+ * Finish the completion. Unblocks all awaiters.
+ */
+static void completion_finish(struct completion *c)
+{
+    pthread_mutex_lock(&c->mu);
+    assert(!c->done && "trying to finish an already finished completion");
+    c->done = true;
+    pthread_cond_broadcast(&c->cond);
+    pthread_mutex_unlock(&c->mu);
+}
+
+struct clone_thread_info {
+    struct completion running;
+    int tid;
+    int (*callback)(void *);
+    void *payload;
+};
+
+static void *clone_thread_run(void *raw_info)
+{
+    struct clone_thread_info *info = (struct clone_thread_info *) raw_info;
+    info->tid = syscall(SYS_gettid);
+
+    /*
+     * Save out callback/payload since lifetime of info is only guaranteed
+     * until we finish the completion.
+     */
+    int (*callback)(void *) = info->callback;
+    void *payload = info->payload;
+    completion_finish(&info->running);
+
+    _exit(callback(payload));
+}
+
+static int clone_thread(int flags, int (*callback)(void *), void *payload)
+{
+    struct clone_thread_info info;
+    pthread_attr_t attr;
+    int ret;
+    pthread_t thread_unused;
+
+    memset(&info, 0, sizeof(info));
+
+    completion_init(&info.running);
+    info.callback = callback;
+    info.payload = payload;
+
+    (void)pthread_attr_init(&attr);
+    (void)pthread_attr_setstacksize(&attr, NEW_STACK_SIZE);
+    (void)pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+
+    ret = pthread_create(&thread_unused, &attr, clone_thread_run, (void *) 
&info);
+    /* pthread_create returns errors directly, instead of via errno. */
+    if (ret != 0) {
+        errno = ret;
+        ret = -1;
+    } else {
+        completion_await(&info.running);
+        ret = info.tid;
+    }
+
+    pthread_attr_destroy(&attr);
+    return ret;
+}
+
+int qemu_clone(int flags, int (*callback)(void *), void *payload)
+{
+    int ret;
+
+    if (clone_flags_are_thread(flags)) {
+        /*
+         * The new process uses the same flags as pthread_create, so we can
+         * use pthread_create directly. This is an optimization.
+         */
+        return clone_thread(flags, callback, payload);
+    }
+
+    if (clone_flags_are_fork(flags)) {
+        /*
+         * Special case a true `fork` clone call. This is so we can take
+         * advantage of special pthread_atfork handlers in libraries we
+         * depend on (e.g., glibc). Without this, existing users of `fork`
+         * in multi-threaded environments will likely get new flaky
+         * deadlocks.
+         */
+        fork_start();
+        ret = fork();
+        if (ret == 0) {
+            fork_end(1);
+            _exit(callback(payload));
+        }
+        fork_end(0);
+        return ret;
+    }
+
+    /* !fork && !thread */
+    errno = EINVAL;
+    return -1;
+}
diff --git a/linux-user/clone.h b/linux-user/clone.h
new file mode 100644
index 0000000000..34ae9b3780
--- /dev/null
+++ b/linux-user/clone.h
@@ -0,0 +1,27 @@
+#ifndef CLONE_H
+#define CLONE_H
+
+/*
+ * qemu_clone executes the given `callback`, with the given payload as the
+ * first argument, in a new process created with the given flags. Some clone
+ * flags, such as *SETTLS, *CLEARTID are not supported. The child thread ID is
+ * returned on success, otherwise negative errno is returned on clone failure.
+ */
+int qemu_clone(int flags, int (*callback)(void *), void *payload);
+
+/* Returns true if the given clone flags can be emulated with libc fork. */
+static bool clone_flags_are_fork(unsigned int flags)
+{
+    return flags == SIGCHLD;
+}
+
+/* Returns true if the given clone flags can be emulated with pthread_create. 
*/
+static bool clone_flags_are_thread(unsigned int flags)
+{
+    return flags == (
+        CLONE_VM | CLONE_FS | CLONE_FILES |
+        CLONE_SIGHAND | CLONE_THREAD | CLONE_SYSVSEM
+    );
+}
+
+#endif /* CLONE_H */
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index 97de9fb5c9..7ce021cea2 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -122,6 +122,7 @@
 #include "qapi/error.h"
 #include "fd-trans.h"
 #include "tcg/tcg.h"
+#include "clone.h"
 
 #ifndef CLONE_IO
 #define CLONE_IO                0x80000000      /* Clone io context */
@@ -135,12 +136,6 @@
  *  * flags we can implement within QEMU itself
  *  * flags we can't support and will return an error for
  */
-/* For thread creation, all these flags must be present; for
- * fork, none must be present.
- */
-#define CLONE_THREAD_FLAGS                              \
-    (CLONE_VM | CLONE_FS | CLONE_FILES |                \
-     CLONE_SIGHAND | CLONE_THREAD | CLONE_SYSVSEM)
 
 /* These flags are ignored:
  * CLONE_DETACHED is now ignored by the kernel;
@@ -150,30 +145,10 @@
     (CLONE_DETACHED | CLONE_IO)
 
 /* Flags for fork which we can implement within QEMU itself */
-#define CLONE_OPTIONAL_FORK_FLAGS               \
+#define CLONE_EMULATED_FLAGS               \
     (CLONE_SETTLS | CLONE_PARENT_SETTID |       \
      CLONE_CHILD_CLEARTID | CLONE_CHILD_SETTID)
 
-/* Flags for thread creation which we can implement within QEMU itself */
-#define CLONE_OPTIONAL_THREAD_FLAGS                             \
-    (CLONE_SETTLS | CLONE_PARENT_SETTID |                       \
-     CLONE_CHILD_CLEARTID | CLONE_CHILD_SETTID | CLONE_PARENT)
-
-#define CLONE_INVALID_FORK_FLAGS                                        \
-    (~(CSIGNAL | CLONE_OPTIONAL_FORK_FLAGS | CLONE_IGNORED_FLAGS))
-
-#define CLONE_INVALID_THREAD_FLAGS                                      \
-    (~(CSIGNAL | CLONE_THREAD_FLAGS | CLONE_OPTIONAL_THREAD_FLAGS |     \
-       CLONE_IGNORED_FLAGS))
-
-/* CLONE_VFORK is special cased early in do_fork(). The other flag bits
- * have almost all been allocated. We cannot support any of
- * CLONE_NEWNS, CLONE_NEWCGROUP, CLONE_NEWUTS, CLONE_NEWIPC,
- * CLONE_NEWUSER, CLONE_NEWPID, CLONE_NEWNET, CLONE_PTRACE, CLONE_UNTRACED.
- * The checks against the invalid thread masks above will catch these.
- * (The one remaining unallocated bit is 0x1000 which used to be CLONE_PID.)
- */
-
 /* Define DEBUG_ERESTARTSYS to force every syscall to be restarted
  * once. This exercises the codepaths for restart.
  */
@@ -1104,7 +1079,7 @@ static inline rlim_t target_to_host_rlim(abi_ulong 
target_rlim)
 {
     abi_ulong target_rlim_swap;
     rlim_t result;
-    
+
     target_rlim_swap = tswapal(target_rlim);
     if (target_rlim_swap == TARGET_RLIM_INFINITY)
         return RLIM_INFINITY;
@@ -1112,7 +1087,7 @@ static inline rlim_t target_to_host_rlim(abi_ulong 
target_rlim)
     result = target_rlim_swap;
     if (target_rlim_swap != (rlim_t)result)
         return RLIM_INFINITY;
-    
+
     return result;
 }
 #endif
@@ -1122,13 +1097,13 @@ static inline abi_ulong host_to_target_rlim(rlim_t rlim)
 {
     abi_ulong target_rlim_swap;
     abi_ulong result;
-    
+
     if (rlim == RLIM_INFINITY || rlim != (abi_long)rlim)
         target_rlim_swap = TARGET_RLIM_INFINITY;
     else
         target_rlim_swap = rlim;
     result = tswapal(target_rlim_swap);
-    
+
     return result;
 }
 #endif
@@ -1615,10 +1590,11 @@ static inline abi_long target_to_host_cmsg(struct 
msghdr *msgh,
     abi_ulong target_cmsg_addr;
     struct target_cmsghdr *target_cmsg, *target_cmsg_start;
     socklen_t space = 0;
-    
+
     msg_controllen = tswapal(target_msgh->msg_controllen);
-    if (msg_controllen < sizeof (struct target_cmsghdr)) 
+    if (msg_controllen < sizeof(struct target_cmsghdr)) {
         goto the_end;
+    }
     target_cmsg_addr = tswapal(target_msgh->msg_control);
     target_cmsg = lock_user(VERIFY_READ, target_cmsg_addr, msg_controllen, 1);
     target_cmsg_start = target_cmsg;
@@ -1703,8 +1679,9 @@ static inline abi_long host_to_target_cmsg(struct 
target_msghdr *target_msgh,
     socklen_t space = 0;
 
     msg_controllen = tswapal(target_msgh->msg_controllen);
-    if (msg_controllen < sizeof (struct target_cmsghdr)) 
+    if (msg_controllen < sizeof(struct target_cmsghdr)) {
         goto the_end;
+    }
     target_cmsg_addr = tswapal(target_msgh->msg_control);
     target_cmsg = lock_user(VERIFY_WRITE, target_cmsg_addr, msg_controllen, 0);
     target_cmsg_start = target_cmsg;
@@ -5750,9 +5727,10 @@ abi_long do_set_thread_area(CPUX86State *env, abi_ulong 
ptr)
     }
     unlock_user_struct(target_ldt_info, ptr, 1);
 
-    if (ldt_info.entry_number < TARGET_GDT_ENTRY_TLS_MIN || 
-        ldt_info.entry_number > TARGET_GDT_ENTRY_TLS_MAX)
-           return -TARGET_EINVAL;
+    if (ldt_info.entry_number < TARGET_GDT_ENTRY_TLS_MIN ||
+        ldt_info.entry_number > TARGET_GDT_ENTRY_TLS_MAX) {
+        return -TARGET_EINVAL;
+    }
     seg_32bit = ldt_info.flags & 1;
     contents = (ldt_info.flags >> 1) & 3;
     read_exec_only = (ldt_info.flags >> 3) & 1;
@@ -5828,7 +5806,7 @@ static abi_long do_get_thread_area(CPUX86State *env, 
abi_ulong ptr)
     lp = (uint32_t *)(gdt_table + idx);
     entry_1 = tswap32(lp[0]);
     entry_2 = tswap32(lp[1]);
-    
+
     read_exec_only = ((entry_2 >> 9) & 1) ^ 1;
     contents = (entry_2 >> 10) & 3;
     seg_not_present = ((entry_2 >> 15) & 1) ^ 1;
@@ -5844,8 +5822,8 @@ static abi_long do_get_thread_area(CPUX86State *env, 
abi_ulong ptr)
         (read_exec_only << 3) | (limit_in_pages << 4) |
         (seg_not_present << 5) | (useable << 6) | (lm << 7);
     limit = (entry_1 & 0xffff) | (entry_2  & 0xf0000);
-    base_addr = (entry_1 >> 16) | 
-        (entry_2 & 0xff000000) | 
+    base_addr = (entry_1 >> 16) |
+        (entry_2 & 0xff000000) |
         ((entry_2 & 0xff) << 16);
     target_ldt_info->base_addr = tswapal(base_addr);
     target_ldt_info->limit = tswap32(limit);
@@ -5895,53 +5873,71 @@ abi_long do_arch_prctl(CPUX86State *env, int code, 
abi_ulong addr)
 
 #endif /* defined(TARGET_I386) */
 
-#define NEW_STACK_SIZE 0x40000
-
-
 static pthread_mutex_t clone_lock = PTHREAD_MUTEX_INITIALIZER;
 typedef struct {
-    CPUArchState *env;
+    /* Used to synchronize thread/process creation between parent and child. */
     pthread_mutex_t mutex;
     pthread_cond_t cond;
-    pthread_t thread;
-    uint32_t tid;
+    /*
+     * Guest pointers for implementing CLONE_PARENT_SETTID
+     * and CLONE_CHILD_SETTID.
+     */
     abi_ulong child_tidptr;
     abi_ulong parent_tidptr;
-    sigset_t sigmask;
-} new_thread_info;
+    struct {
+        sigset_t sigmask;
+        CPUArchState *env;
+        bool register_thread;
+        bool signal_setup;
+    } child;
+} clone_info;
 
-static void *clone_func(void *arg)
+static int clone_run(void *arg)
 {
-    new_thread_info *info = arg;
+    clone_info *info = (clone_info *) arg;
     CPUArchState *env;
     CPUState *cpu;
     TaskState *ts;
+    uint32_t tid;
 
-    rcu_register_thread();
-    tcg_register_thread();
-    env = info->env;
+    if (info->child.register_thread) {
+        rcu_register_thread();
+        tcg_register_thread();
+    }
+
+    env = info->child.env;
     cpu = env_cpu(env);
     thread_cpu = cpu;
     ts = (TaskState *)cpu->opaque;
-    info->tid = sys_gettid();
+    tid = sys_gettid();
     task_settid(ts);
-    if (info->child_tidptr)
-        put_user_u32(info->tid, info->child_tidptr);
-    if (info->parent_tidptr)
-        put_user_u32(info->tid, info->parent_tidptr);
+
     qemu_guest_random_seed_thread_part2(cpu->random_seed);
-    /* Enable signals.  */
-    sigprocmask(SIG_SETMASK, &info->sigmask, NULL);
-    /* Signal to the parent that we're ready.  */
-    pthread_mutex_lock(&info->mutex);
-    pthread_cond_broadcast(&info->cond);
-    pthread_mutex_unlock(&info->mutex);
-    /* Wait until the parent has finished initializing the tls state.  */
-    pthread_mutex_lock(&clone_lock);
-    pthread_mutex_unlock(&clone_lock);
+
+    if (info->parent_tidptr) {
+        /*
+         * Even when memory is not shared, parent_tidptr is set before the
+         * process copy, so we need to set it in the child.
+         */
+        put_user_u32(tid, info->parent_tidptr);
+    }
+
+    if (info->child_tidptr) {
+        put_user_u32(tid, info->child_tidptr);
+    }
+
+    /* Enable signals. */
+    sigprocmask(SIG_SETMASK, &info->child.sigmask, NULL);
+
+    if (info->child.signal_setup) {
+        pthread_mutex_lock(&info->mutex);
+        pthread_cond_broadcast(&info->cond);
+        pthread_mutex_unlock(&info->mutex);
+    }
+
     cpu_loop(env);
     /* never exits */
-    return NULL;
+    _exit(1);  /* avoid compiler warning. */
 }
 
 /* do_fork() Must return host values and target errnos (unlike most
@@ -5951,139 +5947,131 @@ static int do_fork(CPUArchState *env, unsigned int 
flags, abi_ulong newsp,
                    abi_ulong child_tidptr)
 {
     CPUState *cpu = env_cpu(env);
-    int ret;
+    int proc_flags, host_sig, ret;
     TaskState *ts;
     CPUState *new_cpu;
-    CPUArchState *new_env;
-    sigset_t sigmask;
+    sigset_t block_sigmask;
+    sigset_t orig_sigmask;
+    clone_info info;
+    TaskState *parent_ts = (TaskState *)cpu->opaque;
 
-    flags &= ~CLONE_IGNORED_FLAGS;
+    memset(&info, 0, sizeof(info));
+
+    /*
+     * When cloning the actual subprocess, we don't need to worry about any
+     * flags that can be ignored, or emulated in QEMU. proc_flags holds only
+     * the flags that need to be passed to `clone` itself.
+     */
+    proc_flags = flags & ~(CLONE_EMULATED_FLAGS | CLONE_IGNORED_FLAGS);
+
+    /*
+     * The exit signal is included in the flags. That signal needs to be mapped
+     * to the appropriate host signal, and we need to check if that signal is
+     * supported.
+     */
+    host_sig = target_to_host_signal(proc_flags & CSIGNAL);
+    if (host_sig > SIGRTMAX) {
+        qemu_log_mask(LOG_UNIMP,
+                      "guest signal %d not supported for exit_signal",
+                      proc_flags & CSIGNAL);
+        return -TARGET_EINVAL;
+    }
+    proc_flags = (proc_flags & ~CSIGNAL) | host_sig;
 
     /* Emulate vfork() with fork() */
-    if (flags & CLONE_VFORK)
-        flags &= ~(CLONE_VFORK | CLONE_VM);
+    if (proc_flags & CLONE_VFORK) {
+        proc_flags &= ~(CLONE_VFORK | CLONE_VM);
+    }
 
-    if (flags & CLONE_VM) {
-        TaskState *parent_ts = (TaskState *)cpu->opaque;
-        new_thread_info info;
-        pthread_attr_t attr;
+    if (!clone_flags_are_fork(proc_flags) &&
+        !clone_flags_are_thread(proc_flags)) {
+        qemu_log_mask(LOG_UNIMP, "unsupported clone flags");
+        return -TARGET_EINVAL;
+    }
 
-        if (((flags & CLONE_THREAD_FLAGS) != CLONE_THREAD_FLAGS) ||
-            (flags & CLONE_INVALID_THREAD_FLAGS)) {
-            return -TARGET_EINVAL;
-        }
+    pthread_mutex_init(&info.mutex, NULL);
+    pthread_mutex_lock(&info.mutex);
+    pthread_cond_init(&info.cond, NULL);
 
-        ts = g_new0(TaskState, 1);
-        init_task_state(ts);
+    ts = g_new0(TaskState, 1);
+    init_task_state(ts);
 
-        /* Grab a mutex so that thread setup appears atomic.  */
-        pthread_mutex_lock(&clone_lock);
+    /* Guard CPU copy. It is not thread-safe. */
+    pthread_mutex_lock(&clone_lock);
+    info.child.env = cpu_copy(env);
+    pthread_mutex_unlock(&clone_lock);
+    /* Init regs that differ from the parent.  */
+    cpu_clone_regs_child(info.child.env, newsp, flags);
 
-        /* we create a new CPU instance. */
-        new_env = cpu_copy(env);
-        /* Init regs that differ from the parent.  */
-        cpu_clone_regs_child(new_env, newsp, flags);
+    if (flags & CLONE_SETTLS) {
+        cpu_set_tls(info.child.env, newtls);
+    }
+
+    new_cpu = env_cpu(info.child.env);
+    new_cpu->opaque = ts;
+    ts->bprm = parent_ts->bprm;
+    ts->info = parent_ts->info;
+    ts->signal_mask = parent_ts->signal_mask;
+
+    if (flags & CLONE_CHILD_CLEARTID) {
+        ts->child_tidptr = child_tidptr;
+    }
+
+    if (flags & CLONE_CHILD_SETTID) {
+        info.child_tidptr = child_tidptr;
+    }
+    if (flags & CLONE_PARENT_SETTID) {
+        info.parent_tidptr = parent_tidptr;
+    }
+
+    /*
+     * If the child process is going to share memory, and this is our first
+     * such child process or thread, we need to ensure we generate code for
+     * parallel execution and flush old translations.
+     */
+    if (!parallel_cpus && (proc_flags & CLONE_VM)) {
+        parallel_cpus = true;
+        tb_flush(cpu);
+    }
+
+    if (proc_flags & CLONE_VM) {
+        info.child.register_thread = true;
+        info.child.signal_setup = true;
+    }
+
+    /*
+     * It is not safe to deliver signals until the child has finished
+     * initializing, so temporarily block all signals.
+     */
+    sigfillset(&block_sigmask);
+    sigprocmask(SIG_BLOCK, &block_sigmask, &orig_sigmask);
+    info.child.sigmask = orig_sigmask;
+
+    ret = get_errno(qemu_clone(proc_flags, clone_run, (void *) &info));
+
+    if (ret >= 0 && (proc_flags & CLONE_VM)) {
+        /*
+         * Wait for the child to finish setup if the child is running in the
+         * same VM.
+         */
+        pthread_cond_wait(&info.cond, &info.mutex);
+    }
+
+    sigprocmask(SIG_SETMASK, &orig_sigmask, NULL);
+
+    pthread_mutex_unlock(&info.mutex);
+    pthread_cond_destroy(&info.cond);
+    pthread_mutex_destroy(&info.mutex);
+
+    if (ret >= 0 && !(proc_flags & CLONE_VM)) {
+        /*
+         * If !CLONE_VM, then we need to set parent_tidptr, since the child
+         * won't set it for us. Should always be safe to set it here anyways.
+         */
+        put_user_u32(ret, info.parent_tidptr);
         cpu_clone_regs_parent(env, flags);
-        new_cpu = env_cpu(new_env);
-        new_cpu->opaque = ts;
-        ts->bprm = parent_ts->bprm;
-        ts->info = parent_ts->info;
-        ts->signal_mask = parent_ts->signal_mask;
-
-        if (flags & CLONE_CHILD_CLEARTID) {
-            ts->child_tidptr = child_tidptr;
-        }
-
-        if (flags & CLONE_SETTLS) {
-            cpu_set_tls (new_env, newtls);
-        }
-
-        memset(&info, 0, sizeof(info));
-        pthread_mutex_init(&info.mutex, NULL);
-        pthread_mutex_lock(&info.mutex);
-        pthread_cond_init(&info.cond, NULL);
-        info.env = new_env;
-        if (flags & CLONE_CHILD_SETTID) {
-            info.child_tidptr = child_tidptr;
-        }
-        if (flags & CLONE_PARENT_SETTID) {
-            info.parent_tidptr = parent_tidptr;
-        }
-
-        ret = pthread_attr_init(&attr);
-        ret = pthread_attr_setstacksize(&attr, NEW_STACK_SIZE);
-        ret = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
-        /* It is not safe to deliver signals until the child has finished
-           initializing, so temporarily block all signals.  */
-        sigfillset(&sigmask);
-        sigprocmask(SIG_BLOCK, &sigmask, &info.sigmask);
-        cpu->random_seed = qemu_guest_random_seed_thread_part1();
-
-        /* If this is our first additional thread, we need to ensure we
-         * generate code for parallel execution and flush old translations.
-         */
-        if (!parallel_cpus) {
-            parallel_cpus = true;
-            tb_flush(cpu);
-        }
-
-        ret = pthread_create(&info.thread, &attr, clone_func, &info);
-        /* TODO: Free new CPU state if thread creation failed.  */
-
-        sigprocmask(SIG_SETMASK, &info.sigmask, NULL);
-        pthread_attr_destroy(&attr);
-        if (ret == 0) {
-            /* Wait for the child to initialize.  */
-            pthread_cond_wait(&info.cond, &info.mutex);
-            ret = info.tid;
-        } else {
-            ret = -1;
-        }
-        pthread_mutex_unlock(&info.mutex);
-        pthread_cond_destroy(&info.cond);
-        pthread_mutex_destroy(&info.mutex);
-        pthread_mutex_unlock(&clone_lock);
-    } else {
-        /* if no CLONE_VM, we consider it is a fork */
-        if (flags & CLONE_INVALID_FORK_FLAGS) {
-            return -TARGET_EINVAL;
-        }
-
-        /* We can't support custom termination signals */
-        if ((flags & CSIGNAL) != TARGET_SIGCHLD) {
-            return -TARGET_EINVAL;
-        }
-
-        if (block_signals()) {
-            return -TARGET_ERESTARTSYS;
-        }
-
-        fork_start();
-        ret = fork();
-        if (ret == 0) {
-            /* Child Process.  */
-            cpu_clone_regs_child(env, newsp, flags);
-            fork_end(1);
-            /* There is a race condition here.  The parent process could
-               theoretically read the TID in the child process before the child
-               tid is set.  This would require using either ptrace
-               (not implemented) or having *_tidptr to point at a shared memory
-               mapping.  We can't repeat the spinlock hack used above because
-               the child process gets its own copy of the lock.  */
-            if (flags & CLONE_CHILD_SETTID)
-                put_user_u32(sys_gettid(), child_tidptr);
-            if (flags & CLONE_PARENT_SETTID)
-                put_user_u32(sys_gettid(), parent_tidptr);
-            ts = (TaskState *)cpu->opaque;
-            if (flags & CLONE_SETTLS)
-                cpu_set_tls (env, newtls);
-            if (flags & CLONE_CHILD_CLEARTID)
-                ts->child_tidptr = child_tidptr;
-        } else {
-            cpu_clone_regs_parent(env, flags);
-            fork_end(0);
-        }
     }
+
     return ret;
 }
 
@@ -7644,6 +7632,7 @@ static abi_long do_syscall1(void *cpu_env, int num, 
abi_long arg1,
 
     switch(num) {
     case TARGET_NR_exit:
+    {
         /* In old applications this may be used to implement _exit(2).
            However in threaded applictions it is used for thread termination,
            and _exit_group is used for application termination.
@@ -7673,6 +7662,7 @@ static abi_long do_syscall1(void *cpu_env, int num, 
abi_long arg1,
                 do_sys_futex(g2h(ts->child_tidptr), FUTEX_WAKE, INT_MAX,
                           NULL, NULL, 0);
             }
+
             thread_cpu = NULL;
             g_free(ts);
             rcu_unregister_thread();
@@ -7683,6 +7673,7 @@ static abi_long do_syscall1(void *cpu_env, int num, 
abi_long arg1,
         preexit_cleanup(cpu_env, arg1);
         _exit(arg1);
         return 0; /* avoid warning */
+    }
     case TARGET_NR_read:
         if (arg2 == 0 && arg3 == 0) {
             return get_errno(safe_read(arg1, 0, 0));
@@ -9679,9 +9670,10 @@ static abi_long do_syscall1(void *cpu_env, int num, 
abi_long arg1,
         return ret;
 #ifdef __NR_exit_group
         /* new thread calls */
-    case TARGET_NR_exit_group:
+    case TARGET_NR_exit_group: {
         preexit_cleanup(cpu_env, arg1);
         return get_errno(exit_group(arg1));
+    }
 #endif
     case TARGET_NR_setdomainname:
         if (!(p = lock_user_string(arg1)))
@@ -10873,8 +10865,10 @@ static abi_long do_syscall1(void *cpu_env, int num, 
abi_long arg1,
         return get_errno(fchown(arg1, low2highuid(arg2), low2highgid(arg3)));
 #if defined(TARGET_NR_fchownat)
     case TARGET_NR_fchownat:
-        if (!(p = lock_user_string(arg2))) 
+        p = lock_user_string(arg2)
+        if (!p) {
             return -TARGET_EFAULT;
+        }
         ret = get_errno(fchownat(arg1, p, low2highuid(arg3),
                                  low2highgid(arg4), arg5));
         unlock_user(p, arg2, 0);
-- 
2.27.0.290.gba653c62da-goog


Reply via email to