diff options
author | Paul Fulghum <paulkf@microgate.com> | 2006-02-14 16:53:00 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-02-14 19:09:33 -0500 |
commit | da965822abd18a17d7cffe1d511f48951c82dfb6 (patch) | |
tree | b5a64fd33374661dbc689d3192a5c9134b3672e5 /drivers/char/tty_io.c | |
parent | 16bf134840da3920ded1290973c56ec214636f12 (diff) |
[PATCH] tty reference count fix
Fix hole where tty structure can be released when reference count is non
zero. Existing code can sleep without tty_sem protection between deciding
to release the tty structure (setting local variables tty_closing and
otty_closing) and setting TTY_CLOSING to prevent further opens. An open
can occur during this interval causing release_dev() to free the tty
structure while it is still referenced.
This should fix bugzilla.kernel.org [Bug 6041] New: Unable to handle kernel
paging request
In Bug 6041, tty_open() oopes on accessing the tty structure it has
successfully claimed. Bug was on SMP machine with the same tty being
opened and closed by multiple processes, and DEBUG_PAGEALLOC enabled.
Signed-off-by: Paul Fulghum <paulkf@microgate.com>
Cc: Alan Cox <alan@lxorguk.ukuu.org.uk>
Cc: Jesper Juhl <jesper.juhl@gmail.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'drivers/char/tty_io.c')
-rw-r--r-- | drivers/char/tty_io.c | 7 |
1 files changed, 3 insertions, 4 deletions
diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index a23816d3e9a1..e9bba94fc898 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c | |||
@@ -1841,7 +1841,6 @@ static void release_dev(struct file * filp) | |||
1841 | tty_closing = tty->count <= 1; | 1841 | tty_closing = tty->count <= 1; |
1842 | o_tty_closing = o_tty && | 1842 | o_tty_closing = o_tty && |
1843 | (o_tty->count <= (pty_master ? 1 : 0)); | 1843 | (o_tty->count <= (pty_master ? 1 : 0)); |
1844 | up(&tty_sem); | ||
1845 | do_sleep = 0; | 1844 | do_sleep = 0; |
1846 | 1845 | ||
1847 | if (tty_closing) { | 1846 | if (tty_closing) { |
@@ -1869,6 +1868,7 @@ static void release_dev(struct file * filp) | |||
1869 | 1868 | ||
1870 | printk(KERN_WARNING "release_dev: %s: read/write wait queue " | 1869 | printk(KERN_WARNING "release_dev: %s: read/write wait queue " |
1871 | "active!\n", tty_name(tty, buf)); | 1870 | "active!\n", tty_name(tty, buf)); |
1871 | up(&tty_sem); | ||
1872 | schedule(); | 1872 | schedule(); |
1873 | } | 1873 | } |
1874 | 1874 | ||
@@ -1877,8 +1877,6 @@ static void release_dev(struct file * filp) | |||
1877 | * both sides, and we've completed the last operation that could | 1877 | * both sides, and we've completed the last operation that could |
1878 | * block, so it's safe to proceed with closing. | 1878 | * block, so it's safe to proceed with closing. |
1879 | */ | 1879 | */ |
1880 | |||
1881 | down(&tty_sem); | ||
1882 | if (pty_master) { | 1880 | if (pty_master) { |
1883 | if (--o_tty->count < 0) { | 1881 | if (--o_tty->count < 0) { |
1884 | printk(KERN_WARNING "release_dev: bad pty slave count " | 1882 | printk(KERN_WARNING "release_dev: bad pty slave count " |
@@ -1892,7 +1890,6 @@ static void release_dev(struct file * filp) | |||
1892 | tty->count, tty_name(tty, buf)); | 1890 | tty->count, tty_name(tty, buf)); |
1893 | tty->count = 0; | 1891 | tty->count = 0; |
1894 | } | 1892 | } |
1895 | up(&tty_sem); | ||
1896 | 1893 | ||
1897 | /* | 1894 | /* |
1898 | * We've decremented tty->count, so we need to remove this file | 1895 | * We've decremented tty->count, so we need to remove this file |
@@ -1937,6 +1934,8 @@ static void release_dev(struct file * filp) | |||
1937 | read_unlock(&tasklist_lock); | 1934 | read_unlock(&tasklist_lock); |
1938 | } | 1935 | } |
1939 | 1936 | ||
1937 | up(&tty_sem); | ||
1938 | |||
1940 | /* check whether both sides are closing ... */ | 1939 | /* check whether both sides are closing ... */ |
1941 | if (!tty_closing || (o_tty && !o_tty_closing)) | 1940 | if (!tty_closing || (o_tty && !o_tty_closing)) |
1942 | return; | 1941 | return; |