diff options
-rw-r--r-- | sys/kern/kern_exec.c | 58 | ||||
-rw-r--r-- | sys/kern/kern_thread.c | 83 | ||||
-rw-r--r-- | sys/sys/proc.h | 8 |
3 files changed, 111 insertions, 38 deletions
diff --git a/sys/kern/kern_exec.c b/sys/kern/kern_exec.c index 77e7bc5..47b3a33 100644 --- a/sys/kern/kern_exec.c +++ b/sys/kern/kern_exec.c @@ -80,6 +80,8 @@ static int sysctl_kern_usrstack(SYSCTL_HANDLER_ARGS); static int sysctl_kern_stackprot(SYSCTL_HANDLER_ARGS); static int kern_execve(struct thread *td, char *fname, char **argv, char **envv, struct mac *mac_p); +static int do_execve(struct thread *td, char *fname, char **argv, + char **envv, struct mac *mac_p); /* XXX This should be vm_size_t. */ SYSCTL_PROC(_kern, KERN_PS_STRINGS, ps_strings, CTLTYPE_ULONG|CTLFLAG_RD, @@ -205,6 +207,44 @@ __mac_execve(td, uap) #endif } +static int +kern_execve(td, fname, argv, envv, mac_p) + struct thread *td; + char *fname; + char **argv; + char **envv; + struct mac *mac_p; +{ + struct proc *p = td->td_proc; + int error; + + if (p->p_flag & P_HADTHREADS) { + PROC_LOCK(p); + if (thread_single(SINGLE_BOUNDARY)) { + PROC_UNLOCK(p); + return (ERESTART); /* Try again later. */ + } + PROC_UNLOCK(p); + } + + error = do_execve(td, fname, argv, envv, mac_p); + + if (p->p_flag & P_HADTHREADS) { + PROC_LOCK(p); + /* + * If success, we upgrade to SINGLE_EXIT state to + * force other threads to suicide. + */ + if (error == 0) + thread_single(SINGLE_EXIT); + else + thread_single_end(); + PROC_UNLOCK(p); + } + + return (error); +} + /* * In-kernel implementation of execve(). All arguments are assumed to be * userspace pointers from the passed thread. @@ -212,7 +252,7 @@ __mac_execve(td, uap) * MPSAFE */ static int -kern_execve(td, fname, argv, envv, mac_p) +do_execve(td, fname, argv, envv, mac_p) struct thread *td; char *fname; char **argv; @@ -254,16 +294,6 @@ kern_execve(td, fname, argv, envv, mac_p) PROC_LOCK(p); KASSERT((p->p_flag & P_INEXEC) == 0, ("%s(): process already has P_INEXEC flag", __func__)); - if (p->p_flag & P_HADTHREADS) { - if (thread_single(SINGLE_EXIT)) { - PROC_UNLOCK(p); - return (ERESTART); /* Try again later. */ - } - /* - * If we get here all other threads are dead, - * and threading mode has been turned off - */ - } p->p_flag |= P_INEXEC; PROC_UNLOCK(p); @@ -624,9 +654,13 @@ interpret: /* * If tracing the process, trap to debugger so breakpoints * can be set before the program executes. + * Use tdsignal to deliver signal to current thread, use + * psignal may cause the signal to be delivered to wrong thread + * because that thread will exit, remember we are going to enter + * single thread mode. */ if (p->p_flag & P_TRACED) - psignal(p, SIGTRAP); + tdsignal(td, SIGTRAP, SIGTARGET_TD); /* clear "fork but no exec" flag, as we _are_ execing */ p->p_acflag &= ~AFORK; diff --git a/sys/kern/kern_thread.c b/sys/kern/kern_thread.c index e9e3900..835ca80 100644 --- a/sys/kern/kern_thread.c +++ b/sys/kern/kern_thread.c @@ -764,7 +764,7 @@ thread_unlink(struct thread *td) * any sleeping threads that are interruptable. (PCATCH). */ int -thread_single(int force_exit) +thread_single(int mode) { struct thread *td; struct thread *td2; @@ -781,36 +781,53 @@ thread_single(int force_exit) return (0); /* Is someone already single threading? */ - if (p->p_singlethread) + if (p->p_singlethread != NULL && p->p_singlethread != td) return (1); + if (mode == SINGLE_EXIT) { + p->p_flag |= P_SINGLE_EXIT; + p->p_flag &= ~P_SINGLE_BOUNDARY; + } else { + p->p_flag &= ~P_SINGLE_EXIT; + if (mode == SINGLE_BOUNDARY) + p->p_flag |= P_SINGLE_BOUNDARY; + else + p->p_flag &= ~P_SINGLE_BOUNDARY; + } p->p_flag |= P_STOPPED_SINGLE; mtx_lock_spin(&sched_lock); p->p_singlethread = td; - if (force_exit == SINGLE_EXIT) { + if (mode == SINGLE_EXIT) remaining = p->p_numthreads; - p->p_flag |= P_SINGLE_EXIT; - } else { + else if (mode == SINGLE_BOUNDARY) + remaining = p->p_numthreads - p->p_boundary_count; + else remaining = p->p_numthreads - p->p_suspcount; - p->p_flag &= ~P_SINGLE_EXIT; - } while (remaining != 1) { FOREACH_THREAD_IN_PROC(p, td2) { if (td2 == td) continue; td2->td_flags |= TDF_ASTPENDING; if (TD_IS_INHIBITED(td2)) { - if (force_exit == SINGLE_EXIT) { + switch (mode) { + case SINGLE_EXIT: if (td->td_flags & TDF_DBSUSPEND) td->td_flags &= ~TDF_DBSUSPEND; - if (TD_IS_SUSPENDED(td2)) { + if (TD_IS_SUSPENDED(td2)) thread_unsuspend_one(td2); - } if (TD_ON_SLEEPQ(td2) && - (td2->td_flags & TDF_SINTR)) { + (td2->td_flags & TDF_SINTR)) sleepq_abort(td2); - } - } else { + break; + case SINGLE_BOUNDARY: + if (TD_IS_SUSPENDED(td2) && + !(td2->td_flags & TDF_BOUNDARY)) + thread_unsuspend_one(td2); + if (TD_ON_SLEEPQ(td2) && + (td2->td_flags & TDF_SINTR)) + sleepq_abort(td2); + break; + default: if (TD_IS_SUSPENDED(td2)) continue; /* @@ -821,11 +838,14 @@ thread_single(int force_exit) if (td2->td_inhibitors & (TDI_SLEEPING | TDI_SWAPPED)) thread_suspend_one(td2); + break; } } } - if (force_exit == SINGLE_EXIT) + if (mode == SINGLE_EXIT) remaining = p->p_numthreads; + else if (mode == SINGLE_BOUNDARY) + remaining = p->p_numthreads - p->p_boundary_count; else remaining = p->p_numthreads - p->p_suspcount; @@ -845,12 +865,14 @@ thread_single(int force_exit) mtx_unlock_spin(&sched_lock); PROC_LOCK(p); mtx_lock_spin(&sched_lock); - if (force_exit == SINGLE_EXIT) + if (mode == SINGLE_EXIT) remaining = p->p_numthreads; + else if (mode == SINGLE_BOUNDARY) + remaining = p->p_numthreads - p->p_boundary_count; else remaining = p->p_numthreads - p->p_suspcount; } - if (force_exit == SINGLE_EXIT) { + if (mode == SINGLE_EXIT) { /* * We have gotten rid of all the other threads and we * are about to either exit or exec. In either case, @@ -925,6 +947,11 @@ thread_suspend_check(int return_instead) if ((p->p_flag & P_SINGLE_EXIT) && return_instead) return (1); + /* Should we goto user boundary if we didn't come from there? */ + if (P_SHOULDSTOP(p) == P_STOPPED_SINGLE && + (p->p_flag & P_SINGLE_BOUNDARY) && return_instead) + return (1); + mtx_lock_spin(&sched_lock); thread_stopped(p); /* @@ -932,9 +959,8 @@ thread_suspend_check(int return_instead) * this thread should just suicide. * Assumes that P_SINGLE_EXIT implies P_STOPPED_SINGLE. */ - if ((p->p_flag & P_SINGLE_EXIT) && (p->p_singlethread != td)) { + if ((p->p_flag & P_SINGLE_EXIT) && (p->p_singlethread != td)) thread_exit(); - } /* * When a thread suspends, it just @@ -942,13 +968,20 @@ thread_suspend_check(int return_instead) * and stays there. */ thread_suspend_one(td); + if (return_instead == 0) { + p->p_boundary_count++; + td->td_flags |= TDF_BOUNDARY; + } if (P_SHOULDSTOP(p) == P_STOPPED_SINGLE) { - if (p->p_numthreads == p->p_suspcount) { + if (p->p_numthreads == p->p_suspcount) thread_unsuspend_one(p->p_singlethread); - } } PROC_UNLOCK(p); mi_switch(SW_INVOL, NULL); + if (return_instead == 0) { + p->p_boundary_count--; + td->td_flags &= ~TDF_BOUNDARY; + } mtx_unlock_spin(&sched_lock); PROC_LOCK(p); } @@ -1026,7 +1059,7 @@ thread_single_end(void) td = curthread; p = td->td_proc; PROC_LOCK_ASSERT(p, MA_OWNED); - p->p_flag &= ~(P_STOPPED_SINGLE | P_SINGLE_EXIT); + p->p_flag &= ~(P_STOPPED_SINGLE | P_SINGLE_EXIT | P_SINGLE_BOUNDARY); mtx_lock_spin(&sched_lock); p->p_singlethread = NULL; /* @@ -1055,8 +1088,12 @@ thread_sleep_check(struct thread *td) p = td->td_proc; mtx_assert(&sched_lock, MA_OWNED); if (p->p_flag & P_HADTHREADS) { - if ((p->p_flag & P_SINGLE_EXIT) && p->p_singlethread != td) - return (EINTR); + if (p->p_singlethread != td) { + if (p->p_flag & P_SINGLE_EXIT) + return (EINTR); + if (p->p_flag & P_SINGLE_BOUNDARY) + return (ERESTART); + } if (td->td_flags & TDF_INTERRUPT) return (td->td_intrval); } diff --git a/sys/sys/proc.h b/sys/sys/proc.h index 2ed5533..cd00c20 100644 --- a/sys/sys/proc.h +++ b/sys/sys/proc.h @@ -336,7 +336,7 @@ struct thread { #define TDF_UNUSED7 0x00000080 /* --available -- */ #define TDF_TSNOBLOCK 0x00000100 /* Don't block on a turnstile due to race. */ #define TDF_UNUSED9 0x00000200 /* --available -- */ -#define TDF_UNUSED10 0x00000400 /* --available -- */ +#define TDF_BOUNDARY 0x00000400 /* Thread suspended at user boundary */ #define TDF_ASTPENDING 0x00000800 /* Thread has some asynchronous events. */ #define TDF_TIMOFAIL 0x00001000 /* Timeout from sleep after we were awake. */ #define TDF_INTERRUPT 0x00002000 /* Thread is marked as interrupted. */ @@ -573,6 +573,7 @@ struct proc { struct thread *p_singlethread;/* (c + j) If single threading this is it */ int p_suspcount; /* (c) Num threads in suspended mode. */ struct thread *p_xthread; /* (c) Trap thread */ + int p_boundary_count;/* (c) Num threads at user boundary */ /* End area that is zeroed on creation. */ #define p_endzero p_magic @@ -633,7 +634,7 @@ struct proc { #define P_STOPPED_SINGLE 0x80000 /* Only 1 thread can continue (not to user). */ #define P_PROTECTED 0x100000 /* Do not kill on memory overcommit. */ #define P_SIGEVENT 0x200000 /* Process pending signals changed. */ - +#define P_SINGLE_BOUNDARY 0x400000 /* Threads should suspend at user boundary. */ #define P_JAILED 0x1000000 /* Process is in jail. */ #define P_INEXEC 0x4000000 /* Process is in execve(). */ @@ -681,8 +682,9 @@ struct proc { /* How values for thread_single(). */ #define SINGLE_NO_EXIT 0 #define SINGLE_EXIT 1 +#define SINGLE_BOUNDARY 2 -/* XXXKSE: Missing values for thread_signal_check(). */ +/* XXXKSE: Missing values for thread_suspsend_check(). */ #ifdef MALLOC_DECLARE MALLOC_DECLARE(M_PARGS); |