/* When a client clones, we need to keep track of the new thread. This means: 1. allocate a ThreadId+ThreadState+stack for the thread 2. initialize the thread's new VCPU state 3. create the thread using the same args as the client requested, but using the scheduler entrypoint for EIP, and a separate stack for ESP. */ static SysRes do_clone ( ThreadId ptid, ULong flags, Addr rsp, Long* parent_tidptr, Long* child_tidptr, Addr tlsaddr ) { static const Bool debug = False; ThreadId ctid = VG_(alloc_ThreadState)(); ThreadState* ptst = VG_(get_ThreadState)(ptid); ThreadState* ctst = VG_(get_ThreadState)(ctid); UWord* stack; SysRes res; Long rax; vki_sigset_t blockall, savedmask; VG_(sigfillset)(&blockall); vg_assert(VG_(is_running_thread)(ptid)); vg_assert(VG_(is_valid_tid)(ctid)); stack = (UWord*)ML_(allocstack)(ctid); if (stack == NULL) { res = VG_(mk_SysRes_Error)( VKI_ENOMEM ); goto out; } /* Copy register state Both parent and child return to the same place, and the code following the clone syscall works out which is which, so we don't need to worry about it. The parent gets the child's new tid returned from clone, but the child gets 0. If the clone call specifies a NULL rsp for the new thread, then it actually gets a copy of the parent's rsp. */ setup_child( &ctst->arch, &ptst->arch ); /* Make sys_clone appear to have returned Success(0) in the child. */ ctst->arch.vex.guest_RAX = 0; if (rsp != 0) ctst->arch.vex.guest_RSP = rsp; ctst->os_state.parent = ptid; /* inherit signal mask */ ctst->sig_mask = ptst->sig_mask; ctst->tmp_sig_mask = ptst->sig_mask; /* Start the child with its threadgroup being the same as the parent's. This is so that any exit_group calls that happen after the child is created but before it sets its os_state.threadgroup field for real (in thread_wrapper in syswrap-linux.c), really kill the new thread. a.k.a this avoids a race condition in which the thread is unkillable (via exit_group) because its threadgroup is not set. The race window is probably only a few hundred or a few thousand cycles long. See #226116. */ ctst->os_state.threadgroup = ptst->os_state.threadgroup; ML_(guess_and_register_stack) (rsp, ctst); /* Assume the clone will succeed, and tell any tool that wants to know that this thread has come into existence. If the clone fails, we'll send out a ll_exit notification for it at the out: label below, to clean up. */ vg_assert(VG_(owns_BigLock_LL)(ptid)); VG_TRACK ( pre_thread_ll_create, ptid, ctid ); if (flags & VKI_CLONE_SETTLS) { if (debug) VG_(printf)("clone child has SETTLS: tls at %#lx\n", tlsaddr); ctst->arch.vex.guest_FS_CONST = tlsaddr; } flags &= ~VKI_CLONE_SETTLS; /* start the thread with everything blocked */ VG_(sigprocmask)(VKI_SIG_SETMASK, &blockall, &savedmask); /* Create the new thread */ rax = do_syscall_clone_amd64_linux( ML_(start_thread_NORETURN), stack, flags, &VG_(threads)[ctid], child_tidptr, parent_tidptr, NULL ); res = VG_(mk_SysRes_amd64_linux)( rax ); VG_(sigprocmask)(VKI_SIG_SETMASK, &savedmask, NULL); out: if (sr_isError(res)) { /* clone failed */ VG_(cleanup_thread)(&ctst->arch); ctst->status = VgTs_Empty; /* oops. Better tell the tool the thread exited in a hurry :-) */ VG_TRACK( pre_thread_ll_exit, ctid ); } return res; }
/* When a client clones, we need to keep track of the new thread. This means: 1. allocate a ThreadId+ThreadState+stack for the the thread 2. initialize the thread's new VCPU state 3. create the thread using the same args as the client requested, but using the scheduler entrypoint for EIP, and a separate stack for ESP. */ static SysRes do_clone ( ThreadId ptid, ULong flags, Addr rsp, Long* parent_tidptr, Long* child_tidptr, Addr tlsaddr ) { static const Bool debug = False; ThreadId ctid = VG_(alloc_ThreadState)(); ThreadState* ptst = VG_(get_ThreadState)(ptid); ThreadState* ctst = VG_(get_ThreadState)(ctid); UWord* stack; NSegment* seg; SysRes res; Long rax; vki_sigset_t blockall, savedmask; VG_(sigfillset)(&blockall); vg_assert(VG_(is_running_thread)(ptid)); vg_assert(VG_(is_valid_tid)(ctid)); stack = (UWord*)ML_(allocstack)(ctid); if (stack == NULL) { res = VG_(mk_SysRes_Error)( VKI_ENOMEM ); goto out; } /* Copy register state Both parent and child return to the same place, and the code following the clone syscall works out which is which, so we don't need to worry about it. The parent gets the child's new tid returned from clone, but the child gets 0. If the clone call specifies a NULL rsp for the new thread, then it actually gets a copy of the parent's rsp. */ setup_child( &ctst->arch, &ptst->arch ); /* Make sys_clone appear to have returned Success(0) in the child. */ ctst->arch.vex.guest_RAX = 0; if (rsp != 0) ctst->arch.vex.guest_RSP = rsp; ctst->os_state.parent = ptid; /* inherit signal mask */ ctst->sig_mask = ptst->sig_mask; ctst->tmp_sig_mask = ptst->sig_mask; /* We don't really know where the client stack is, because its allocated by the client. The best we can do is look at the memory mappings and try to derive some useful information. We assume that esp starts near its highest possible value, and can only go down to the start of the mmaped segment. */ seg = VG_(am_find_nsegment)((Addr)rsp); if (seg && seg->kind != SkResvn) { ctst->client_stack_highest_word = (Addr)VG_PGROUNDUP(rsp); ctst->client_stack_szB = ctst->client_stack_highest_word - seg->start; if (debug) VG_(printf)("tid %d: guessed client stack range %p-%p\n", ctid, seg->start, VG_PGROUNDUP(rsp)); } else { VG_(message)(Vg_UserMsg, "!? New thread %d starts with RSP(%p) unmapped\n", ctid, rsp); ctst->client_stack_szB = 0; } if (flags & VKI_CLONE_SETTLS) { if (debug) VG_(printf)("clone child has SETTLS: tls at %p\n", tlsaddr); ctst->arch.vex.guest_FS_ZERO = tlsaddr; } flags &= ~VKI_CLONE_SETTLS; /* start the thread with everything blocked */ VG_(sigprocmask)(VKI_SIG_SETMASK, &blockall, &savedmask); /* Create the new thread */ rax = do_syscall_clone_amd64_linux( ML_(start_thread_NORETURN), stack, flags, &VG_(threads)[ctid], child_tidptr, parent_tidptr, NULL ); res = VG_(mk_SysRes_amd64_linux)( rax ); VG_(sigprocmask)(VKI_SIG_SETMASK, &savedmask, NULL); out: if (res.isError) { /* clone failed */ VG_(cleanup_thread)(&ctst->arch); ctst->status = VgTs_Empty; } return res; }