[PATCH] Workaround possible pty line discipline race

Linux Kernel Mailing List <[email protected]> Sat, 12 Mar 2005 12:50:55 +0000
Newsgroups gmane.linux.kernel.commits.2-4
Message-ID <[email protected]>
ChangeSet 1.1576, 2005/03/12 09:50:55-03:00, [email protected]

	[PATCH] Workaround possible pty line discipline race
	
	It's in no way "correct", in that the race hasn't actually gone away by
	this patch, but the patch makes it unimportant. We may end up calling a
	stale line discipline, which is still very wrong, but it so happens that
	we don't much care in practice.
	
	I think that in a 2.4.x tree there are some theoretical SMP races with
	module unloading etc (which the 2.6.x code doesn't have because module
	unload stops the other CPU's - maybe that part got backported to 2.4.x?),
	but quite frankly, I suspect that even in 2.4.x they are entirely
	theoretical and impossible to actually hit.
	
	And again, in theory some line discipline might do something strange in
	it's "chars_in_buffer" routine that would be problematic. In practice
	that's just not the case: the "chars_in_buffer()" routine might return a
	bogus _value_ for a stale line discipline thing, but none of them seem to
	follow any pointers that might have become invalid (and in fact, most
	ldiscs don't even have that function).
	
	So while this patch is wrong in theory, it does have the advantage of
	being (a) very safe minimal patch and (b) fixing the problem in practice
	with no performance downside.
	
	I still feel a bit guilty about it, though.



 pty.c |    6 ++++--
 1 files changed, 4 insertions(+), 2 deletions(-)


diff -Nru a/drivers/char/pty.c b/drivers/char/pty.c
--- a/drivers/char/pty.c	2005-03-12 10:03:18 -08:00
+++ b/drivers/char/pty.c	2005-03-12 10:03:18 -08:00
@@ -218,13 +218,15 @@
 static int pty_chars_in_buffer(struct tty_struct *tty)
 {
 	struct tty_struct *to = tty->link;
+	ssize_t (*chars_in_buffer)(struct tty_struct *);
 	int count;
 
-	if (!to || !to->ldisc.chars_in_buffer)
+	/* We should get the line discipline lock for "tty->link" */
+	if (!to || !(chars_in_buffer = to->ldisc.chars_in_buffer))
 		return 0;
 
 	/* The ldisc must report 0 if no characters available to be read */
-	count = to->ldisc.chars_in_buffer(to);
+	count = chars_in_buffer(to);
 
 	if (tty->driver.subtype == PTY_TYPE_SLAVE) return count;