kernel -- syscons: Resolve unmatched PHOLD() for MOUSE_MODE ioctl.
authorVenkatesh Srinivas <me@endeavour.zapto.org>
Thu, 21 Mar 2013 07:50:52 +0000 (03:50 -0400)
committerSascha Wildner <saw@online.de>
Thu, 21 Mar 2013 18:35:28 +0000 (19:35 +0100)
When a syscons process put itself into MOUSE_MODE, a process-hold
was being placed to stabilize the process for signal delivery. The
hold was not being released on process exit, however.

This change reworks syscons to install a per-process flag as to
whether it is in MOUSE_MODE or not and to remove the excess hold on
exit(). It also cleans up the error handling and prevents stale
process pointers from lingering in syscons stat structures.

Reported-by: mneumann, Studbolt
Closes-bug: 2521

sys/dev/misc/syscons/Makefile
sys/dev/misc/syscons/scmouse.c
sys/sys/proc.h

index 99d0fe1..c8d785a 100644 (file)
@@ -1,5 +1,4 @@
 # $FreeBSD: src/sys/modules/syscons/Makefile,v 1.11.2.2 2003/05/15 02:02:39 murray Exp $
 # $FreeBSD: src/sys/modules/syscons/Makefile,v 1.11.2.2 2003/05/15 02:02:39 murray Exp $
-# $DragonFly: src/sys/dev/misc/syscons/Makefile,v 1.5 2007/08/09 02:27:51 swildner Exp $
 #
 
 .include "../../../platform/${MACHINE_PLATFORM}/Makefile.inc"
 #
 
 .include "../../../platform/${MACHINE_PLATFORM}/Makefile.inc"
index b78b7b2..0cb4dc8 100644 (file)
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  * $FreeBSD: src/sys/dev/syscons/scmouse.c,v 1.12.2.3 2001/07/28 12:51:47 yokota Exp $
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  * $FreeBSD: src/sys/dev/syscons/scmouse.c,v 1.12.2.3 2001/07/28 12:51:47 yokota Exp $
- * $DragonFly: src/sys/dev/misc/syscons/scmouse.c,v 1.14 2008/08/10 19:47:31 swildner Exp $
  */
 
 #include "opt_syscons.h"
 
 #include <sys/param.h>
 #include <sys/systm.h>
  */
 
 #include "opt_syscons.h"
 
 #include <sys/param.h>
 #include <sys/systm.h>
+#include <sys/kernel.h>
 #include <sys/conf.h>
 #include <sys/signalvar.h>
 #include <sys/proc.h>
 #include <sys/tty.h>
 #include <sys/thread2.h>
 #include <sys/conf.h>
 #include <sys/signalvar.h>
 #include <sys/proc.h>
 #include <sys/tty.h>
 #include <sys/thread2.h>
+#include <sys/mplock2.h>
 
 #include <machine/console.h>
 #include <sys/mouse.h>
 
 #include <machine/console.h>
 #include <sys/mouse.h>
 
 #ifndef SC_NO_SYSMOUSE
 
 
 #ifndef SC_NO_SYSMOUSE
 
-/* local variables */
 static int             cut_buffer_size;
 static u_char          *cut_buffer;
 
 /* local functions */
 static int             cut_buffer_size;
 static u_char          *cut_buffer;
 
 /* local functions */
+static void sc_mouse_init(void *);
+static void sc_mouse_uninit(void *);
+
 static void set_mouse_pos(scr_stat *scp);
 #ifndef SC_NO_CUTPASTE
 static int skip_spc_right(scr_stat *scp, int p);
 static void set_mouse_pos(scr_stat *scp);
 #ifndef SC_NO_CUTPASTE
 static int skip_spc_right(scr_stat *scp, int p);
@@ -567,13 +570,56 @@ mouse_paste(scr_stat *scp)
 
 #endif /* SC_NO_CUTPASTE */
 
 
 #endif /* SC_NO_CUTPASTE */
 
+static void
+sc_mouse_exit1_proc(struct proc *p)
+{
+    scr_stat *scp;
+
+    scp = p->p_drv_priv;
+    KKASSERT(scp != NULL);
+
+    get_mplock();
+    KKASSERT(scp->mouse_proc == p);
+    KKASSERT(scp->mouse_pid == p->p_pid);
+
+    scp->mouse_signal = 0;
+    scp->mouse_proc = NULL;
+    scp->mouse_pid = 0;
+    rel_mplock();
+
+    PRELE(p);
+    p->p_flags &= ~P_SCMOUSE;
+    p->p_drv_priv = NULL;
+}
+
+/*
+ * sc_mouse_exit1:
+ *
+ *     Handle exit1 for processes registered as MOUSE_MODE handlers.
+ *     We must remove a process hold, established when MOUSE_MODE
+ *     was enabled.
+ */
+static void
+sc_mouse_exit1(struct thread *td)
+{
+    struct proc *p;
+
+    p = td->td_proc;
+    KKASSERT(p != NULL);
+
+    if ((p->p_flags & P_SCMOUSE) == 0)
+       return;
+
+
+    sc_mouse_exit1_proc(p);
+}
+
 int
 sc_mouse_ioctl(struct tty *tp, u_long cmd, caddr_t data, int flag)
 {
     mouse_info_t *mouse;
     scr_stat *cur_scp;
     scr_stat *scp;
 int
 sc_mouse_ioctl(struct tty *tp, u_long cmd, caddr_t data, int flag)
 {
     mouse_info_t *mouse;
     scr_stat *cur_scp;
     scr_stat *scp;
-    struct proc *oproc;
     int f;
 
     scp = SC_STAT(tp->t_dev);
     int f;
 
     scp = SC_STAT(tp->t_dev);
@@ -581,28 +627,57 @@ sc_mouse_ioctl(struct tty *tp, u_long cmd, caddr_t data, int flag)
     switch (cmd) {
 
     case CONS_MOUSECTL:                /* control mouse arrow */
     switch (cmd) {
 
     case CONS_MOUSECTL:                /* control mouse arrow */
-       mouse = (mouse_info_t*)data;
+       mouse = (mouse_info_t*) data;
        cur_scp = scp->sc->cur_scp;
 
        switch (mouse->operation) {
        cur_scp = scp->sc->cur_scp;
 
        switch (mouse->operation) {
+       /*
+        * Setup a process to receive signals on mouse events.
+        */
        case MOUSE_MODE:
        case MOUSE_MODE:
-           if (ISSIGVALID(mouse->u.mode.signal)) {
-               oproc = scp->mouse_proc;
-               scp->mouse_signal = mouse->u.mode.signal;
-               scp->mouse_proc = curproc;
-               scp->mouse_pid = curproc->p_pid;
-               PHOLD(curproc);
+           get_mplock();
+
+           if (!ISSIGVALID(mouse->u.mode.signal)) {
+               /* Setting MOUSE_MODE w/ an invalid signal is used to disarm */
+               if (scp->mouse_proc == curproc) {
+                   sc_mouse_exit1_proc(curproc);
+                   rel_mplock();
+                   return 0;
+               } else {
+                   rel_mplock();
+                   return EINVAL;
+               }
            } else {
            } else {
-               oproc = scp->mouse_proc;
-               scp->mouse_signal = 0;
-               scp->mouse_proc = NULL;
-               scp->mouse_pid = 0;
-           }
-           if (oproc) {
-                   PRELE(oproc);
-                   oproc = NULL;
-           }
-           return 0;
+               /* Only one mouse process per syscons */
+               if (scp->mouse_proc) {
+                   rel_mplock();
+                   return EINVAL;
+               }
+
+               /* Only one syscons signal source per process */
+               if (curproc->p_flags & P_SCMOUSE) {
+                   rel_mplock();
+                   return EINVAL;
+               }
+
+               /*
+                * Process is stabilized by a hold, which is removed from
+                * sc_mouse_exit1. scp's mouse_{signal,proc,pid} fields
+                * are synchronized by the MP Lock.
+                */
+               scp->mouse_signal = mouse->u.mode.signal;
+               scp->mouse_proc = curproc;
+               scp->mouse_pid = curproc->p_pid;
+               curproc->p_flags |= P_SCMOUSE;
+               KKASSERT(curproc->p_drv_priv == NULL);
+               curproc->p_drv_priv = scp;
+               PHOLD(curproc);
+
+               rel_mplock();
+               return 0;
+            }
+           /*NOTREACHED*/
+           break;
 
        case MOUSE_SHOW:
            crit_enter();
 
        case MOUSE_SHOW:
            crit_enter();
@@ -692,21 +767,14 @@ sc_mouse_ioctl(struct tty *tp, u_long cmd, caddr_t data, int flag)
 
            cur_scp->status &= ~MOUSE_HIDDEN;
 
 
            cur_scp->status &= ~MOUSE_HIDDEN;
 
+           get_mplock();
            if (cur_scp->mouse_signal) {
            if (cur_scp->mouse_signal) {
-               /* has controlling process died? */
-               if (cur_scp->mouse_proc && 
-                   (cur_scp->mouse_proc != pfindn(cur_scp->mouse_pid))){
-                       oproc = cur_scp->mouse_proc;
-                       cur_scp->mouse_signal = 0;
-                       cur_scp->mouse_proc = NULL;
-                       cur_scp->mouse_pid = 0;
-                       if (oproc)
-                               PRELE(oproc);
-               } else {
-                   ksignal(cur_scp->mouse_proc, cur_scp->mouse_signal);
-                   break;
-               }
+               KKASSERT(cur_scp->mouse_proc != NULL);
+               ksignal(cur_scp->mouse_proc, cur_scp->mouse_signal);
+               rel_mplock();
+               break;
            }
            }
+           rel_mplock();
 
            if (ISGRAPHSC(cur_scp) || (cut_buffer == NULL))
                break;
 
            if (ISGRAPHSC(cur_scp) || (cut_buffer == NULL))
                break;
@@ -749,20 +817,14 @@ sc_mouse_ioctl(struct tty *tp, u_long cmd, caddr_t data, int flag)
 
            cur_scp->status &= ~MOUSE_HIDDEN;
 
 
            cur_scp->status &= ~MOUSE_HIDDEN;
 
+           get_mplock();
            if (cur_scp->mouse_signal) {
            if (cur_scp->mouse_signal) {
-               if (cur_scp->mouse_proc && 
-                   (cur_scp->mouse_proc != pfindn(cur_scp->mouse_pid))){
-                       oproc = cur_scp->mouse_proc;
-                       cur_scp->mouse_signal = 0;
-                       cur_scp->mouse_proc = NULL;
-                       cur_scp->mouse_pid = 0;
-                       if (oproc)
-                               PRELE(oproc);
-               } else {
-                   ksignal(cur_scp->mouse_proc, cur_scp->mouse_signal);
-                   break;
-               }
+               KKASSERT(cur_scp->mouse_proc != NULL);
+               ksignal(cur_scp->mouse_proc, cur_scp->mouse_signal);
+               rel_mplock();
+               break;
            }
            }
+           rel_mplock();
 
            if (ISGRAPHSC(cur_scp) || (cut_buffer == NULL))
                break;
 
            if (ISGRAPHSC(cur_scp) || (cut_buffer == NULL))
                break;
@@ -839,4 +901,18 @@ sc_mouse_ioctl(struct tty *tp, u_long cmd, caddr_t data, int flag)
     return ENOIOCTL;
 }
 
     return ENOIOCTL;
 }
 
+void
+sc_mouse_init(void *unused)
+{
+    at_exit(sc_mouse_exit1);
+}
+
+void
+sc_mouse_uninit(void *unused)
+{
+}
+
+SYSINIT(sc_mouse_init, SI_SUB_DRIVERS, SI_ORDER_ANY, sc_mouse_init, NULL);
+SYSUNINIT(sc_mouse_uninit, SI_SUB_DRIVERS, SI_ORDER_ANY, sc_mouse_uninit, NULL);
+
 #endif /* SC_NO_SYSMOUSE */
 #endif /* SC_NO_SYSMOUSE */
index bcb6efb..3047485 100644 (file)
@@ -333,6 +333,7 @@ struct      proc {
        struct spinlock p_spin;         /* Spinlock for LWP access to proc */
        struct lwkt_token p_token;      /* Token for LWP access to proc */
        struct sem_undo *p_sem_undo;    /* Fast semaphore tracking lookup */
        struct spinlock p_spin;         /* Spinlock for LWP access to proc */
        struct lwkt_token p_token;      /* Token for LWP access to proc */
        struct sem_undo *p_sem_undo;    /* Fast semaphore tracking lookup */
+       void            *p_drv_priv;    /* scp linkage (for syscons) */
 };
 
 #define lwp_wchan      lwp_thread->td_wchan
 };
 
 #define lwp_wchan      lwp_thread->td_wchan
@@ -348,7 +349,8 @@ struct      proc {
 #define        P_PPWAIT        0x00010 /* Parent is waiting for child to exec/exit */
 #define        P_PROFIL        0x00020 /* Has started profiling */
 #define P_POSTEXIT     0x00040 /* Prevent procfs from stepping after this pt */
 #define        P_PPWAIT        0x00010 /* Parent is waiting for child to exec/exit */
 #define        P_PROFIL        0x00020 /* Has started profiling */
 #define P_POSTEXIT     0x00040 /* Prevent procfs from stepping after this pt */
-#define        P_UNUSED7       0x00080 /* was: Sleep is interruptible */
+#define        P_SCMOUSE       0x00080 /* Process is held by syscons ioctl */
+                               /* was: Sleep is interruptible */
 #define        P_SUGID         0x00100 /* Had set id privileges since last exec */
 #define        P_SYSTEM        0x00200 /* System proc: no sigs, stats or swapping */
 #define        P_UNUSED10      0x00400 /* was: SIGSTOP status */
 #define        P_SUGID         0x00100 /* Had set id privileges since last exec */
 #define        P_SYSTEM        0x00200 /* System proc: no sigs, stats or swapping */
 #define        P_UNUSED10      0x00400 /* was: SIGSTOP status */