kernel - Fix panic during coredump
authorMatthew Dillon <dillon@apollo.backplane.com>
Fri, 10 Jul 2015 07:37:32 +0000 (00:37 -0700)
committerMatthew Dillon <dillon@apollo.backplane.com>
Fri, 10 Jul 2015 07:37:32 +0000 (00:37 -0700)
* multi-threaded coredumps were not stopping all other threads before
  attempting to scan the vm_map, resulting in numerous possible panics.

* Add a new process state, SCORE, indicating that a core dump is in progress
  and adjust proc_stop() and friends as well as any code which tests the
  SSTOP state.  SCORE overrides SSTOP.

* The coredump code actively waits for all running threads to stop before
  proceeding.

* Prevent a deadlock between a SIGKILL and core dump in progress by
  temporarily counting the master exit thread as a stopped thread (which
  allows the coredump to proceed and finish).

Reported-by: marino
sys/kern/kern_checkpoint.c
sys/kern/kern_exit.c
sys/kern/kern_sig.c
sys/kern/kern_synch.c
sys/kern/sys_process.c
sys/platform/pc64/x86_64/trap.c
sys/platform/vkernel64/x86_64/trap.c
sys/sys/proc.h
sys/vfs/procfs/procfs_ctl.c
sys/vm/vm_pageout.c

index eec5778..70746e7 100644 (file)
@@ -695,11 +695,15 @@ ckpt_freeze_proc(struct lwp *lp, struct file *fp)
         PRINTF(("calling generic_elf_coredump\n"));
        limit = p->p_rlimit[RLIMIT_CORE].rlim_cur;
        if (limit) {
-               proc_stop(p);
-               while (p->p_nstopped < p->p_nthreads - 1)
-                       tsleep(&p->p_nstopped, 0, "freeze", 1);
-               error = generic_elf_coredump(lp, SIGCKPT, fp, limit);
-               proc_unstop(p);
+               if (p->p_stat != SCORE) {
+                       proc_stop(p, SCORE);
+                       while (p->p_nstopped < p->p_nthreads - 1)
+                               tsleep(&p->p_nstopped, 0, "freeze", 1);
+                       error = generic_elf_coredump(lp, SIGCKPT, fp, limit);
+                       proc_unstop(p, SCORE);
+               } else {
+                       error = ERANGE;
+               }
        } else {
                error = ERANGE;
        }
index bc8c8e6..2bdecfd 100644 (file)
@@ -192,6 +192,7 @@ killalllwps(int forexec)
 {
        struct lwp *lp = curthread->td_lwp;
        struct proc *p = lp->lwp_proc;
+       int fakestop;
 
        /*
         * Interlock against P_WEXIT.  Only one of the process's thread
@@ -201,6 +202,19 @@ killalllwps(int forexec)
                return (EALREADY);
        p->p_flags |= P_WEXIT;
 
+       /*
+        * Set temporary stopped state in case we are racing a coredump.
+        * Otherwise the coredump may hang forever.
+        */
+       if (lp->lwp_mpflags & LWP_MP_WSTOP) {
+               fakestop = 0;
+       } else {
+               atomic_set_int(&lp->lwp_mpflags, LWP_MP_WSTOP);
+               ++p->p_nstopped;
+               fakestop = 1;
+               wakeup(&p->p_nstopped);
+       }
+
        /*
         * Interlock with LWP_MP_WEXIT and kill any remaining LWPs
         */
@@ -208,6 +222,14 @@ killalllwps(int forexec)
        if (p->p_nthreads > 1)
                killlwps(lp);
 
+       /*
+        * Undo temporary stopped state
+        */
+       if (fakestop) {
+               atomic_clear_int(&lp->lwp_mpflags, LWP_MP_WSTOP);
+               --p->p_nstopped;
+       }
+
        /*
         * If doing this for an exec, clean up the remaining thread
         * (us) for continuing operation after all the other threads
@@ -893,7 +915,7 @@ loop:
         * the CONT when both are stopped and continued together.  This little
         * two-line hack restores this effect.
         */
-       while (q->p_stat == SSTOP)
+       while (q->p_stat == SSTOP || q->p_stat == SCORE)
             tstop();
 
        nfound = 0;
@@ -1074,7 +1096,8 @@ loop:
                        error = 0;
                        goto done;
                }
-               if (p->p_stat == SSTOP && (p->p_flags & P_WAITED) == 0 &&
+               if ((p->p_stat == SSTOP || p->p_stat == SCORE) &&
+                   (p->p_flags & P_WAITED) == 0 &&
                    ((p->p_flags & P_TRACED) || (options & WUNTRACED))) {
                        PHOLD(p);
                        lwkt_gettoken(&p->p_token);
@@ -1083,7 +1106,7 @@ loop:
                                PRELE(p);
                                goto loop;
                        }
-                       if (p->p_stat != SSTOP ||
+                       if ((p->p_stat != SSTOP && p->p_stat != SCORE) ||
                            (p->p_flags & P_WAITED) != 0 ||
                            ((p->p_flags & P_TRACED) == 0 &&
                             (options & WUNTRACED) == 0)) {
index 8c2038d..bbbd7b5 100644 (file)
@@ -79,6 +79,7 @@ static void   lwp_signotify(struct lwp *lp);
 static void    lwp_signotify_remote(void *arg);
 static int     kern_sigtimedwait(sigset_t set, siginfo_t *info,
                    struct timespec *timeout);
+static void    proc_stopwait(struct proc *p);
 
 static int     filt_sigattach(struct knote *kn);
 static void    filt_sigdetach(struct knote *kn);
@@ -1212,7 +1213,7 @@ lwpsignal(struct proc *p, struct lwp *lp, int sig)
                 * make the process runnable.
                 */
                if (sig == SIGKILL) {
-                       proc_unstop(p);
+                       proc_unstop(p, SSTOP);
                        goto active_process;
                }
 
@@ -1242,7 +1243,7 @@ lwpsignal(struct proc *p, struct lwp *lp, int sig)
                        wakeup(q);
                        if (action == SIG_DFL)
                                SIGDELSET(p->p_siglist, sig);
-                       proc_unstop(p);
+                       proc_unstop(p, SSTOP);
                        lwkt_reltoken(&q->p_token);
                        PRELE(q);
                        if (action == SIG_CATCH)
@@ -1341,7 +1342,7 @@ active_process:
                 */
                if ((p->p_flags & P_WEXIT) == 0) {
                        p->p_xstat = sig;
-                       proc_stop(p);
+                       proc_stop(p, SSTOP);
                }
                goto out;
        }
@@ -1481,18 +1482,26 @@ lwp_signotify_remote(void *arg)
  * Caller must hold p->p_token
  */
 void
-proc_stop(struct proc *p)
+proc_stop(struct proc *p, int sig)
 {
        struct proc *q;
        struct lwp *lp;
 
        ASSERT_LWKT_TOKEN_HELD(&p->p_token);
 
-       /* If somebody raced us, be happy with it */
-       if (p->p_stat == SSTOP || p->p_stat == SZOMB) {
-               return;
+       /*
+        * If somebody raced us, be happy with it.  SCORE overrides SSTOP.
+        */
+       if (sig == SCORE) {
+               if (p->p_stat == SCORE || p->p_stat == SZOMB)
+                       return;
+       } else {
+               if (p->p_stat == SSTOP || p->p_stat == SCORE ||
+                   p->p_stat == SZOMB) {
+                       return;
+               }
        }
-       p->p_stat = SSTOP;
+       p->p_stat = sig;
 
        FOREACH_LWP_IN_PROC(lp, p) {
                LWPHOLD(lp);
@@ -1555,13 +1564,13 @@ proc_stop(struct proc *p)
  * Caller must hold p_token
  */
 void
-proc_unstop(struct proc *p)
+proc_unstop(struct proc *p, int sig)
 {
        struct lwp *lp;
 
        ASSERT_LWKT_TOKEN_HELD(&p->p_token);
 
-       if (p->p_stat != SSTOP)
+       if (p->p_stat != sig)
                return;
 
        p->p_stat = SACTIVE;
@@ -1624,6 +1633,21 @@ proc_unstop(struct proc *p)
        wakeup(p);
 }
 
+/*
+ * Wait for all threads except the current thread to stop.
+ */
+static void
+proc_stopwait(struct proc *p)
+{
+       while ((p->p_stat == SSTOP || p->p_stat == SCORE) &&
+              p->p_nstopped < p->p_nthreads - 1) {
+               tsleep_interlock(&p->p_nstopped, 0);
+               if (p->p_nstopped < p->p_nthreads - 1) {
+                       tsleep(&p->p_nstopped, PINTERLOCKED, "stopwt", hz);
+               }
+       }
+}
+
 /* 
  * No requirements.
  */
@@ -1868,7 +1892,7 @@ issignal(struct lwp *lp, int maytrace)
                /*
                 * If this process is supposed to stop, stop this thread.
                 */
-               if (p->p_stat == SSTOP)
+               if (p->p_stat == SSTOP || p->p_stat == SCORE)
                        tstop();
 
                mask = lwp_sigpend(lp);
@@ -1908,7 +1932,7 @@ issignal(struct lwp *lp, int maytrace)
                         * XXX not sure if this is still true
                         */
                        p->p_xstat = sig;
-                       proc_stop(p);
+                       proc_stop(p, SSTOP);
                        do {
                                tstop();
                        } while (!trace_req(p) && (p->p_flags & P_TRACED));
@@ -1990,7 +2014,7 @@ issignal(struct lwp *lp, int maytrace)
                                        break;  /* == ignore */
                                if ((p->p_flags & P_WEXIT) == 0) {
                                        p->p_xstat = sig;
-                                       proc_stop(p);
+                                       proc_stop(p, SSTOP);
                                        tstop();
                                }
                                break;
@@ -2166,14 +2190,26 @@ sigexit(struct lwp *lp, int sig)
        p->p_acflag |= AXSIG;
        if (sigprop(sig) & SA_CORE) {
                lp->lwp_sig = sig;
+
+               /*
+                * All threads must be stopped before we can safely coredump.
+                * Stop threads using SCORE, which cannot be overridden.
+                */
+               if (p->p_stat != SCORE) {
+                       proc_stop(p, SCORE);
+                       proc_stopwait(p);
+
+                       if (coredump(lp, sig) == 0)
+                               sig |= WCOREFLAG;
+                       p->p_stat = SSTOP;
+               }
+
                /*
                 * Log signals which would cause core dumps
                 * (Log as LOG_INFO to appease those who don't want
                 * these messages.)
                 * XXX : Todo, as well as euid, write out ruid too
                 */
-               if (coredump(lp, sig) == 0)
-                       sig |= WCOREFLAG;
                if (kern_logsigexit)
                        log(LOG_INFO,
                            "pid %d (%s), uid %d: exited on signal %d%s\n",
index f863948..67f6b1e 100644 (file)
@@ -1163,7 +1163,7 @@ tstop(void)
                        PRELE(q);
                }
        }
-       while (p->p_stat == SSTOP) {
+       while (p->p_stat == SSTOP || p->p_stat == SCORE) {
                lp->lwp_stat = LSSTOP;
                tsleep(p, 0, "stop", 0);
        }
index 26d04cd..dccbeff 100644 (file)
@@ -506,7 +506,7 @@ kern_ptrace(struct proc *curp, int req, pid_t pid, void *addr,
                crit_enter();
                if (p->p_stat == SSTOP) {
                        p->p_xstat = data;
-                       proc_unstop(p);
+                       proc_unstop(p, SSTOP);
                } else if (data) {
                        ksignal(p, data);
                }
index c320ac2..0d00f6e 100644 (file)
@@ -247,7 +247,7 @@ recheck:
        /*
         * Block here if we are in a stopped state.
         */
-       if (p->p_stat == SSTOP || dump_stop_usertds) {
+       if (p->p_stat == SSTOP || p->p_stat == SCORE || dump_stop_usertds) {
                lwkt_gettoken(&p->p_token);
                tstop();
                lwkt_reltoken(&p->p_token);
@@ -331,7 +331,8 @@ userexit(struct lwp *lp)
         * Handle stop requests at kernel priority.  Any requests queued
         * after this loop will generate another AST.
         */
-       while (lp->lwp_proc->p_stat == SSTOP) {
+       while (lp->lwp_proc->p_stat == SSTOP ||
+              lp->lwp_proc->p_stat == SCORE) {
                lwkt_gettoken(&lp->lwp_proc->p_token);
                tstop();
                lwkt_reltoken(&lp->lwp_proc->p_token);
index 7410cb7..8f19054 100644 (file)
@@ -223,7 +223,7 @@ recheck:
        /*
         * Block here if we are in a stopped state.
         */
-       if (p->p_stat == SSTOP) {
+       if (p->p_stat == SSTOP || p->p_stat == SCORE) {
                lwkt_gettoken(&p->p_token);
                tstop();
                lwkt_reltoken(&p->p_token);
@@ -306,7 +306,8 @@ userexit(struct lwp *lp)
         * Handle stop requests at kernel priority.  Any requests queued
         * after this loop will generate another AST.
         */
-       while (lp->lwp_proc->p_stat == SSTOP) {
+       while (lp->lwp_proc->p_stat == SSTOP ||
+              lp->lwp_proc->p_stat == SCORE) {
                lwkt_gettoken(&lp->lwp_proc->p_token);
                tstop();
                lwkt_reltoken(&lp->lwp_proc->p_token);
index c326be5..fda1290 100644 (file)
@@ -163,6 +163,7 @@ enum procstat {
        SACTIVE = 2,
        SSTOP = 3,
        SZOMB = 4,
+       SCORE = 5,
 };
 
 struct lwp {
@@ -546,8 +547,8 @@ void        pgrpinsertinit(struct pgrp *pg);
 void   relscurproc(struct proc *curp);
 int    p_trespass (struct ucred *cr1, struct ucred *cr2);
 void   setrunnable (struct lwp *);
-void   proc_stop (struct proc *);
-void   proc_unstop (struct proc *);
+void   proc_stop (struct proc *, int);
+void   proc_unstop (struct proc *, int);
 void   sleep_gdinit (struct globaldata *);
 thread_t cpu_heavy_switch (struct thread *);
 thread_t cpu_lwkt_switch (struct thread *);
index 588b05d..4a866ed 100644 (file)
@@ -158,7 +158,7 @@ procfs_control(struct proc *curp, struct lwp *lp, int op)
                        p->p_oppid = p->p_pptr->p_pid;
                        proc_reparent(p, curp);
                }
-               proc_stop(p);
+               proc_stop(p, SSTOP);
                return (0);
        }
 
@@ -281,7 +281,7 @@ procfs_control(struct proc *curp, struct lwp *lp, int op)
         * that might be in progress.
         */
        if (p->p_stat == SSTOP)
-               proc_unstop(p);
+               proc_unstop(p, SSTOP);
        return (0);
 }
 
@@ -331,7 +331,7 @@ procfs_doctl(struct proc *curp, struct lwp *lp, struct pfsnode *pfs,
                                 * Make the process runnable but do not
                                 * break its tsleep.
                                 */
-                               proc_unstop(p);
+                               proc_unstop(p, SSTOP);
                        } else {
                                ksignal(p, nm->nm_val);
                        }
index 755fdc0..09caf95 100644 (file)
@@ -1528,7 +1528,7 @@ vm_pageout_scan_callback(struct proc *p, void *data)
         * if the process is in a non-running type state,
         * don't touch it.
         */
-       if (p->p_stat != SACTIVE && p->p_stat != SSTOP) {
+       if (p->p_stat != SACTIVE && p->p_stat != SSTOP && p->p_stat != SCORE) {
                lwkt_reltoken(&p->p_token);
                return (0);
        }
@@ -2123,7 +2123,7 @@ vm_daemon_callback(struct proc *p, void *data __unused)
         * if the process is in a non-running type state,
         * don't touch it.
         */
-       if (p->p_stat != SACTIVE && p->p_stat != SSTOP) {
+       if (p->p_stat != SACTIVE && p->p_stat != SSTOP && p->p_stat != SCORE) {
                lwkt_reltoken(&p->p_token);
                return (0);
        }