aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNick Piggin <npiggin@kernel.dk>2010-08-17 14:37:36 -0400
committerAl Viro <viro@zeniv.linux.org.uk>2010-08-18 08:35:47 -0400
commitd996b62a8df1d935b01319bf8defb95b5709f7b8 (patch)
treed81f8240da776336845a2063555d7bb4dce684bd
parentee2ffa0dfdd2db19705f2ba1c6a4c0bfe8122dd8 (diff)
tty: fix fu_list abuse
tty: fix fu_list abuse tty code abuses fu_list, which causes a bug in remount,ro handling. If a tty device node is opened on a filesystem, then the last link to the inode removed, the filesystem will be allowed to be remounted readonly. This is because fs_may_remount_ro does not find the 0 link tty inode on the file sb list (because the tty code incorrectly removed it to use for its own purpose). This can result in a filesystem with errors after it is marked "clean". Taking idea from Christoph's initial patch, allocate a tty private struct at file->private_data and put our required list fields in there, linking file and tty. This makes tty nodes behave the same way as other device nodes and avoid meddling with the vfs, and avoids this bug. The error handling is not trivial in the tty code, so for this bugfix, I take the simple approach of using __GFP_NOFAIL and don't worry about memory errors. This is not a problem because our allocator doesn't fail small allocs as a rule anyway. So proper error handling is left as an exercise for tty hackers. [ Arguably filesystem's device inode would ideally be divorced from the driver's pseudo inode when it is opened, but in practice it's not clear whether that will ever be worth implementing. ] Cc: linux-kernel@vger.kernel.org Cc: Christoph Hellwig <hch@infradead.org> Cc: Alan Cox <alan@lxorguk.ukuu.org.uk> Cc: Greg Kroah-Hartman <gregkh@suse.de> Signed-off-by: Nick Piggin <npiggin@kernel.dk> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
-rw-r--r--drivers/char/pty.c6
-rw-r--r--drivers/char/tty_io.c84
-rw-r--r--fs/internal.h2
-rw-r--r--include/linux/fs.h2
-rw-r--r--include/linux/tty.h8
-rw-r--r--security/selinux/hooks.c5
6 files changed, 69 insertions, 38 deletions
diff --git a/drivers/char/pty.c b/drivers/char/pty.c
index 2c64faa8efa4..c350d01716bd 100644
--- a/drivers/char/pty.c
+++ b/drivers/char/pty.c
@@ -675,12 +675,8 @@ static int ptmx_open(struct inode *inode, struct file *filp)
675 } 675 }
676 676
677 set_bit(TTY_PTY_LOCK, &tty->flags); /* LOCK THE SLAVE */ 677 set_bit(TTY_PTY_LOCK, &tty->flags); /* LOCK THE SLAVE */
678 filp->private_data = tty;
679 678
680 file_sb_list_del(filp); /* __dentry_open has put it on the sb list */ 679 tty_add_file(tty, filp);
681 spin_lock(&tty_files_lock);
682 list_add(&filp->f_u.fu_list, &tty->tty_files);
683 spin_unlock(&tty_files_lock);
684 680
685 retval = devpts_pty_new(inode, tty->link); 681 retval = devpts_pty_new(inode, tty->link);
686 if (retval) 682 if (retval)
diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c
index cd5b829634ea..949067a0bd47 100644
--- a/drivers/char/tty_io.c
+++ b/drivers/char/tty_io.c
@@ -188,6 +188,41 @@ void free_tty_struct(struct tty_struct *tty)
188 kfree(tty); 188 kfree(tty);
189} 189}
190 190
191static inline struct tty_struct *file_tty(struct file *file)
192{
193 return ((struct tty_file_private *)file->private_data)->tty;
194}
195
196/* Associate a new file with the tty structure */
197void tty_add_file(struct tty_struct *tty, struct file *file)
198{
199 struct tty_file_private *priv;
200
201 /* XXX: must implement proper error handling in callers */
202 priv = kmalloc(sizeof(*priv), GFP_KERNEL|__GFP_NOFAIL);
203
204 priv->tty = tty;
205 priv->file = file;
206 file->private_data = priv;
207
208 spin_lock(&tty_files_lock);
209 list_add(&priv->list, &tty->tty_files);
210 spin_unlock(&tty_files_lock);
211}
212
213/* Delete file from its tty */
214void tty_del_file(struct file *file)
215{
216 struct tty_file_private *priv = file->private_data;
217
218 spin_lock(&tty_files_lock);
219 list_del(&priv->list);
220 spin_unlock(&tty_files_lock);
221 file->private_data = NULL;
222 kfree(priv);
223}
224
225
191#define TTY_NUMBER(tty) ((tty)->index + (tty)->driver->name_base) 226#define TTY_NUMBER(tty) ((tty)->index + (tty)->driver->name_base)
192 227
193/** 228/**
@@ -500,6 +535,7 @@ void __tty_hangup(struct tty_struct *tty)
500 struct file *cons_filp = NULL; 535 struct file *cons_filp = NULL;
501 struct file *filp, *f = NULL; 536 struct file *filp, *f = NULL;
502 struct task_struct *p; 537 struct task_struct *p;
538 struct tty_file_private *priv;
503 int closecount = 0, n; 539 int closecount = 0, n;
504 unsigned long flags; 540 unsigned long flags;
505 int refs = 0; 541 int refs = 0;
@@ -509,7 +545,7 @@ void __tty_hangup(struct tty_struct *tty)
509 545
510 546
511 spin_lock(&redirect_lock); 547 spin_lock(&redirect_lock);
512 if (redirect && redirect->private_data == tty) { 548 if (redirect && file_tty(redirect) == tty) {
513 f = redirect; 549 f = redirect;
514 redirect = NULL; 550 redirect = NULL;
515 } 551 }
@@ -524,7 +560,8 @@ void __tty_hangup(struct tty_struct *tty)
524 560
525 spin_lock(&tty_files_lock); 561 spin_lock(&tty_files_lock);
526 /* This breaks for file handles being sent over AF_UNIX sockets ? */ 562 /* This breaks for file handles being sent over AF_UNIX sockets ? */
527 list_for_each_entry(filp, &tty->tty_files, f_u.fu_list) { 563 list_for_each_entry(priv, &tty->tty_files, list) {
564 filp = priv->file;
528 if (filp->f_op->write == redirected_tty_write) 565 if (filp->f_op->write == redirected_tty_write)
529 cons_filp = filp; 566 cons_filp = filp;
530 if (filp->f_op->write != tty_write) 567 if (filp->f_op->write != tty_write)
@@ -892,12 +929,10 @@ static ssize_t tty_read(struct file *file, char __user *buf, size_t count,
892 loff_t *ppos) 929 loff_t *ppos)
893{ 930{
894 int i; 931 int i;
895 struct tty_struct *tty; 932 struct inode *inode = file->f_path.dentry->d_inode;
896 struct inode *inode; 933 struct tty_struct *tty = file_tty(file);
897 struct tty_ldisc *ld; 934 struct tty_ldisc *ld;
898 935
899 tty = file->private_data;
900 inode = file->f_path.dentry->d_inode;
901 if (tty_paranoia_check(tty, inode, "tty_read")) 936 if (tty_paranoia_check(tty, inode, "tty_read"))
902 return -EIO; 937 return -EIO;
903 if (!tty || (test_bit(TTY_IO_ERROR, &tty->flags))) 938 if (!tty || (test_bit(TTY_IO_ERROR, &tty->flags)))
@@ -1068,12 +1103,11 @@ void tty_write_message(struct tty_struct *tty, char *msg)
1068static ssize_t tty_write(struct file *file, const char __user *buf, 1103static ssize_t tty_write(struct file *file, const char __user *buf,
1069 size_t count, loff_t *ppos) 1104 size_t count, loff_t *ppos)
1070{ 1105{
1071 struct tty_struct *tty;
1072 struct inode *inode = file->f_path.dentry->d_inode; 1106 struct inode *inode = file->f_path.dentry->d_inode;
1107 struct tty_struct *tty = file_tty(file);
1108 struct tty_ldisc *ld;
1073 ssize_t ret; 1109 ssize_t ret;
1074 struct tty_ldisc *ld;
1075 1110
1076 tty = file->private_data;
1077 if (tty_paranoia_check(tty, inode, "tty_write")) 1111 if (tty_paranoia_check(tty, inode, "tty_write"))
1078 return -EIO; 1112 return -EIO;
1079 if (!tty || !tty->ops->write || 1113 if (!tty || !tty->ops->write ||
@@ -1510,13 +1544,13 @@ static void release_tty(struct tty_struct *tty, int idx)
1510 1544
1511int tty_release(struct inode *inode, struct file *filp) 1545int tty_release(struct inode *inode, struct file *filp)
1512{ 1546{
1513 struct tty_struct *tty, *o_tty; 1547 struct tty_struct *tty = file_tty(filp);
1548 struct tty_struct *o_tty;
1514 int pty_master, tty_closing, o_tty_closing, do_sleep; 1549 int pty_master, tty_closing, o_tty_closing, do_sleep;
1515 int devpts; 1550 int devpts;
1516 int idx; 1551 int idx;
1517 char buf[64]; 1552 char buf[64];
1518 1553
1519 tty = filp->private_data;
1520 if (tty_paranoia_check(tty, inode, "tty_release_dev")) 1554 if (tty_paranoia_check(tty, inode, "tty_release_dev"))
1521 return 0; 1555 return 0;
1522 1556
@@ -1674,11 +1708,7 @@ int tty_release(struct inode *inode, struct file *filp)
1674 * - do_tty_hangup no longer sees this file descriptor as 1708 * - do_tty_hangup no longer sees this file descriptor as
1675 * something that needs to be handled for hangups. 1709 * something that needs to be handled for hangups.
1676 */ 1710 */
1677 spin_lock(&tty_files_lock); 1711 tty_del_file(filp);
1678 BUG_ON(list_empty(&filp->f_u.fu_list));
1679 list_del_init(&filp->f_u.fu_list);
1680 spin_unlock(&tty_files_lock);
1681 filp->private_data = NULL;
1682 1712
1683 /* 1713 /*
1684 * Perform some housekeeping before deciding whether to return. 1714 * Perform some housekeeping before deciding whether to return.
@@ -1845,12 +1875,8 @@ got_driver:
1845 return PTR_ERR(tty); 1875 return PTR_ERR(tty);
1846 } 1876 }
1847 1877
1848 filp->private_data = tty; 1878 tty_add_file(tty, filp);
1849 BUG_ON(list_empty(&filp->f_u.fu_list)); 1879
1850 file_sb_list_del(filp); /* __dentry_open has put it on the sb list */
1851 spin_lock(&tty_files_lock);
1852 list_add(&filp->f_u.fu_list, &tty->tty_files);
1853 spin_unlock(&tty_files_lock);
1854 check_tty_count(tty, "tty_open"); 1880 check_tty_count(tty, "tty_open");