kern/127446: [patch] fix race in sys/dev/kbdmux/kbdmux.c

Eygene Ryabinkin rea-fbsd at codelabs.ru
Wed Sep 17 16:20:02 UTC 2008


>Number:         127446
>Category:       kern
>Synopsis:       [patch] fix race in sys/dev/kbdmux/kbdmux.c
>Confidential:   no
>Severity:       critical
>Priority:       high
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Wed Sep 17 16:20:02 UTC 2008
>Closed-Date:
>Last-Modified:
>Originator:     Eygene Ryabinkin
>Release:        FreeBSD 7.1-PRERELEASE amd64
>Organization:
Code Labs
>Environment:

System: FreeBSD XXX 7.1-PRERELEASE FreeBSD 7.1-PRERELEASE #55: Wed Sep 17 19:57:25 MSD 2008 root at XXX:/usr/src/sys/amd64/compile/XXX amd64

CVSupped system yesterday late at the evening (aroung 17:00 UTC).

>Description:

Since kbdmux(4) is not MPSAFE now, its interrupt routines are running
under Giant.  But there is kbdmux_read_char() routine that runs unlocked
and can race with the interrupt handler.  When there is no input data
in the keyboard queue and kbdmux(4) is in the POLLING mode, routine will
try to poll each mux member for the scancode.  And if user presses the
key at that time, KBDMUX_READ_CHAR() can race with the interrupt handler
kbdmux_kbd_event() since we don't lock polling loop.

>How-To-Repeat:

I see this on my laptop: sometimes during boot, when system asks me for
the password of geli(8)-encrypted volume, it doubles the symbols or even
panics.  I don't see this on the other systems, so perhaps my laptop is
just so lucky ;))

But one can try to enable echoing of the input symbols during the geli's
bootup password dialog (setting g_eli_visible_passphrase to 1
unconditionally) and maybe symbols will be doubled.  I see this issue
only during boot-up phase, but I feel that this is due to the fact that
for the rest of the system's operation only interrupt handler is
working, at least I see it from the debug printf()s.

>Fix:

The following patch fixes the things for me.  It just acquires Giant for
the time of polling.  I did a limited testing at my systems and there
were no signs of regressions yet.

Seems like in the properly locked situation (with non-dummy KBDMUX_LOCK
and KBDMUX_UNLOCK) this issue will vanish, so I had conditionalized
Giant grabbing.

--- kbdmux-read-race.patch begins here ---
--- sys/dev/kbdmux/kbdmux.c.orig	2008-09-17 10:41:00.000000000 +0400
+++ sys/dev/kbdmux/kbdmux.c	2008-09-17 19:52:00.000000000 +0400
@@ -79,6 +79,10 @@
  */
 
 #if 0 /* not yet */
+#define KBDMUX_LOCK_POLLER(dummy)
+
+#define KBDMUX_UNLOCK_POLLER(dummy)
+
 #define KBDMUX_LOCK_DECL_GLOBAL \
 	struct mtx ks_lock
 #define KBDMUX_LOCK_INIT(s) \
@@ -98,6 +102,10 @@
 #define KBDMUX_QUEUE_INTR(s) \
 	taskqueue_enqueue(taskqueue_swi_giant, &(s)->ks_task)
 #else
+#define	KBDMUX_LOCK_POLLER(dummy) \
+	mtx_lock(&Giant)
+#define	KBDMUX_UNLOCK_POLLER(dummy) \
+	mtx_unlock(&Giant)
 #define KBDMUX_LOCK_DECL_GLOBAL
 
 #define KBDMUX_LOCK_INIT(s)
@@ -661,6 +669,14 @@
 		if (state->ks_flags & POLLING) {
 			kbdmux_kbd_t	*k;
 
+			/*
+			 * Grab Giant: we don't want to race with
+			 * the keyboard interrupt handler.  And this
+			 * can happen, because when a key will be
+			 * pressed, our READ_CHAR will be competing
+			 * with the kbdmux_kbd_event()'s one.
+			 */
+			KBDMUX_LOCK_POLLER();
 			SLIST_FOREACH(k, &state->ks_kbds, next) {
 				while (KBDMUX_CHECK_CHAR(k->kbd)) {
 					scancode = KBDMUX_READ_CHAR(k->kbd, 0);
@@ -674,6 +690,7 @@
 					putc(scancode, &state->ks_inq);
 				}
 			}
+			KBDMUX_UNLOCK_POLLER();
 
 			if (state->ks_inq.c_cc > 0)
 				goto next_code;
--- kbdmux-read-race.patch ends here ---
>Release-Note:
>Audit-Trail:
>Unformatted:


More information about the freebsd-bugs mailing list