[PATCH] Remove race between con_open and con_close

[ Same race and same patch also by Steven Rostedt <rostedt@goodmis.org> ]

I have a laptop (G3 powerbook) which will pretty reliably hit a race
between con_open and con_close late in the boot process and oops in
vt_ioctl due to tty->driver_data being NULL.

What happens is this: process A opens /dev/tty6; it comes into
con_open() (drivers/char/vt.c) and assign a non-NULL value to
tty->driver_data.  Then process A closes that and concurrently process
B opens /dev/tty6.  Process A gets through con_close() and clears
tty->driver_data, since tty->count == 1.  However, before process A
can decrement tty->count, we switch to process B (e.g. at the
down(&tty_sem) call at drivers/char/tty_io.c line 1626).

So process B gets to run and comes into con_open with tty->count == 2,
as tty->count is incremented (in init_dev) before con_open is called.
Because tty->count != 1, we don't set tty->driver_data.  Then when the
process tries to do anything with that fd, it oopses.

The simple and effective fix for this is to test tty->driver_data
rather than tty->count in con_open.  The testing and setting of
tty->driver_data is serialized with respect to the clearing of
tty->driver_data in con_close by the console_sem.  We can't get a
situation where con_open sees tty->driver_data != NULL and then
con_close on a different fd clears tty->driver_data, because
tty->count is incremented before con_open is called.  Thus this patch
eliminates the race, and in fact with this patch my laptop doesn't
oops.

Signed-off-by: Paul Mackerras <paulus@samba.org>
[ Same patch
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
  in http://marc.theaimsgroup.com/?l=linux-kernel&m=112450820432121&w=2 ]
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
diff --git a/drivers/char/vt.c b/drivers/char/vt.c
index 30d9673..665103c 100644
--- a/drivers/char/vt.c
+++ b/drivers/char/vt.c
@@ -2433,7 +2433,7 @@
 	int ret = 0;
 
 	acquire_console_sem();
-	if (tty->count == 1) {
+	if (tty->driver_data == NULL) {
 		ret = vc_allocate(currcons);
 		if (ret == 0) {
 			struct vc_data *vc = vc_cons[currcons].d;