use-after-free races have been seen due to the workqueue timer in the tty
structure going off after the tty was freed.

Fix that up by adding a new workqueue function to synchronously delete the
timer, use that in the tty layer.

Also add a might_sleep() check to flush_workqueue().  It usually will not
sleep, so the check will accelerate bug hunting.

And change the flush_workqueue() sleep to be uninterruptible.  We worry that
delivery of a signal may cause the wait to return too early.



 25-akpm/drivers/char/tty_io.c     |    9 ++++++++-
 25-akpm/include/linux/workqueue.h |   10 ++++++++++
 25-akpm/kernel/workqueue.c        |    4 +++-
 3 files changed, 21 insertions(+), 2 deletions(-)

diff -puN drivers/char/tty_io.c~tty-shutdown-race-fix drivers/char/tty_io.c
--- 25/drivers/char/tty_io.c~tty-shutdown-race-fix	Wed Apr  2 16:14:12 2003
+++ 25-akpm/drivers/char/tty_io.c	Wed Apr  2 16:20:36 2003
@@ -1286,7 +1286,14 @@ static void release_dev(struct file * fi
 	}
 	
 	/*
-	 * Make sure that the tty's task queue isn't activated. 
+	 * Prevent flush_to_ldisc() from rescheduling the work for later.  Then
+	 * kill any delayed work.
+	 */
+	clear_bit(TTY_DONT_FLIP, &tty->flags);
+	cancel_delayed_work(&tty->flip.work);
+
+	/*
+	 * Wait for ->hangup_work and ->flip.work handlers to terminate
 	 */
 	flush_scheduled_work();
 
diff -puN include/linux/workqueue.h~tty-shutdown-race-fix include/linux/workqueue.h
--- 25/include/linux/workqueue.h~tty-shutdown-race-fix	Wed Apr  2 16:16:17 2003
+++ 25-akpm/include/linux/workqueue.h	Wed Apr  2 16:18:58 2003
@@ -63,5 +63,15 @@ extern int current_is_keventd(void);
 
 extern void init_workqueues(void);
 
+/*
+ * Kill off a pending schedule_delayed_work().  Note that the work callback
+ * function may still be running on return from cancel_delayed_work().  Run
+ * flush_scheduled_work() to wait on it.
+ */
+static inline int cancel_delayed_work(struct work_struct *work)
+{
+	return del_timer_sync(&work->timer);
+}
+
 #endif
 
diff -puN kernel/workqueue.c~tty-shutdown-race-fix kernel/workqueue.c
--- 25/kernel/workqueue.c~tty-shutdown-race-fix	Wed Apr  2 16:21:04 2003
+++ 25-akpm/kernel/workqueue.c	Wed Apr  2 16:31:08 2003
@@ -231,6 +231,8 @@ void flush_workqueue(struct workqueue_st
 	struct cpu_workqueue_struct *cwq;
 	int cpu;
 
+	might_sleep();
+
 	for (cpu = 0; cpu < NR_CPUS; cpu++) {
 		if (!cpu_online(cpu))
 			continue;
@@ -246,7 +248,7 @@ void flush_workqueue(struct workqueue_st
 			 * Wait for helper thread(s) to finish up
 			 * the queue:
 			 */
-			set_task_state(current, TASK_INTERRUPTIBLE);
+			set_current_state(TASK_UNINTERRUPTIBLE);
 			add_wait_queue(&cwq->work_done, &wait);
 			if (atomic_read(&cwq->nr_queued))
 				schedule();

_