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");
1855 if (tty->driver->type == TTY_DRIVER_TYPE_PTY && 1881 if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
1856 tty->driver->subtype == PTY_TYPE_MASTER) 1882 tty->driver->subtype == PTY_TYPE_MASTER)
@@ -1926,11 +1952,10 @@ got_driver:
1926 1952
1927static unsigned int tty_poll(struct file *filp, poll_table *wait) 1953static unsigned int tty_poll(struct file *filp, poll_table *wait)
1928{ 1954{
1929 struct tty_struct *tty; 1955 struct tty_struct *tty = file_tty(filp);
1930 struct tty_ldisc *ld; 1956 struct tty_ldisc *ld;
1931 int ret = 0; 1957 int ret = 0;
1932 1958
1933 tty = filp->private_data;
1934 if (tty_paranoia_check(tty, filp->f_path.dentry->d_inode, "tty_poll")) 1959 if (tty_paranoia_check(tty, filp->f_path.dentry->d_inode, "tty_poll"))
1935 return 0; 1960 return 0;
1936 1961
@@ -1943,11 +1968,10 @@ static unsigned int tty_poll(struct file *filp, poll_table *wait)
1943 1968
1944static int __tty_fasync(int fd, struct file *filp, int on) 1969static int __tty_fasync(int fd, struct file *filp, int on)
1945{ 1970{
1946 struct tty_struct *tty; 1971 struct tty_struct *tty = file_tty(filp);
1947 unsigned long flags; 1972 unsigned long flags;
1948 int retval = 0; 1973 int retval = 0;
1949 1974
1950 tty = filp->private_data;
1951 if (tty_paranoia_check(tty, filp->f_path.dentry->d_inode, "tty_fasync")) 1975 if (tty_paranoia_check(tty, filp->f_path.dentry->d_inode, "tty_fasync"))
1952 goto out; 1976 goto out;
1953 1977
@@ -2501,13 +2525,13 @@ EXPORT_SYMBOL(tty_pair_get_pty);
2501 */ 2525 */
2502long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 2526long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
2503{ 2527{
2504 struct tty_struct *tty, *real_tty; 2528 struct tty_struct *tty = file_tty(file);
2529 struct tty_struct *real_tty;
2505 void __user *p = (void __user *)arg; 2530 void __user *p = (void __user *)arg;
2506 int retval; 2531 int retval;
2507 struct tty_ldisc *ld; 2532 struct tty_ldisc *ld;
2508 struct inode *inode = file->f_dentry->d_inode; 2533 struct inode *inode = file->f_dentry->d_inode;
2509 2534
2510 tty = file->private_data;
2511 if (tty_paranoia_check(tty, inode, "tty_ioctl")) 2535 if (tty_paranoia_check(tty, inode, "tty_ioctl"))
2512 return -EINVAL; 2536 return -EINVAL;
2513 2537
@@ -2629,7 +2653,7 @@ static long tty_compat_ioctl(struct file *file, unsigned int cmd,
2629 unsigned long arg) 2653 unsigned long arg)
2630{ 2654{
2631 struct inode *inode = file->f_dentry->d_inode; 2655 struct inode *inode = file->f_dentry->d_inode;
2632 struct tty_struct *tty = file->private_data; 2656 struct tty_struct *tty = file_tty(file);
2633 struct tty_ldisc *ld; 2657 struct tty_ldisc *ld;
2634 int retval = -ENOIOCTLCMD; 2658 int retval = -ENOIOCTLCMD;
2635 2659
@@ -2721,7 +2745,7 @@ void __do_SAK(struct tty_struct *tty)
2721 if (!filp) 2745 if (!filp)
2722 continue; 2746 continue;
2723 if (filp->f_op->read == tty_read && 2747 if (filp->f_op->read == tty_read &&
2724 filp->private_data == tty) { 2748 file_tty(filp) == tty) {
2725 printk(KERN_NOTICE "SAK: killed process %d" 2749 printk(KERN_NOTICE "SAK: killed process %d"
2726 " (%s): fd#%d opened to the tty\n", 2750 " (%s): fd#%d opened to the tty\n",
2727 task_pid_nr(p), p->comm, i); 2751 task_pid_nr(p), p->comm, i);
diff --git a/fs/internal.h b/fs/internal.h
index 6b706bc60a66..6a5c13a80660 100644
--- a/fs/internal.h
+++ b/fs/internal.h
@@ -80,6 +80,8 @@ extern void chroot_fs_refs(struct path *, struct path *);
80/* 80/*
81 * file_table.c 81 * file_table.c
82 */ 82 */
83extern void file_sb_list_add(struct file *f, struct super_block *sb);
84extern void file_sb_list_del(struct file *f);
83extern void mark_files_ro(struct super_block *); 85extern void mark_files_ro(struct super_block *);
84extern struct file *get_empty_filp(void); 86extern struct file *get_empty_filp(void);
85 87
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 5a9a9e5a3705..5e65add0f163 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -2185,8 +2185,6 @@ static inline void insert_inode_hash(struct inode *inode) {
2185 __insert_inode_hash(inode, inode->i_ino); 2185 __insert_inode_hash(inode, inode->i_ino);
2186} 2186}
2187 2187
2188extern void file_sb_list_add(struct file *f, struct super_block *sb);
2189extern void file_sb_list_del(struct file *f);
2190#ifdef CONFIG_BLOCK 2188#ifdef CONFIG_BLOCK
2191extern void submit_bio(int, struct bio *); 2189extern void submit_bio(int, struct bio *);
2192extern int bdev_read_only(struct block_device *); 2190extern int bdev_read_only(struct block_device *);
diff --git a/include/linux/tty.h b/include/linux/tty.h
index f6b371a2514e..67d64e6efe7a 100644
--- a/include/linux/tty.h
+++ b/include/linux/tty.h
@@ -329,6 +329,13 @@ struct tty_struct {
329 struct tty_port *port; 329 struct tty_port *port;
330}; 330};
331 331
332/* Each of a tty's open files has private_data pointing to tty_file_private */
333struct tty_file_private {
334 struct tty_struct *tty;
335 struct file *file;
336 struct list_head list;
337};
338
332/* tty magic number */ 339/* tty magic number */
333#define TTY_MAGIC 0x5401 340#define TTY_MAGIC 0x5401
334 341
@@ -458,6 +465,7 @@ extern void proc_clear_tty(struct task_struct *p);
458extern struct tty_struct *get_current_tty(void); 465extern struct tty_struct *get_current_tty(void);
459extern void tty_default_fops(struct file_operations *fops); 466extern void tty_default_fops(struct file_operations *fops);
460extern struct tty_struct *alloc_tty_struct(void); 467extern struct tty_struct *alloc_tty_struct(void);
468extern void tty_add_file(struct tty_struct *tty, struct file *file);
461extern void free_tty_struct(struct tty_struct *tty); 469extern void free_tty_struct(struct tty_struct *tty);
462extern void initialize_tty_struct(struct tty_struct *tty, 470extern void initialize_tty_struct(struct tty_struct *tty,
463 struct tty_driver *driver, int idx); 471 struct tty_driver *driver, int idx);
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index bd7da0f0ccf3..4796ddd4e721 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -2172,6 +2172,7 @@ static inline void flush_unauthorized_files(const struct cred *cred,
2172 if (tty) { 2172 if (tty) {
2173 spin_lock(&tty_files_lock); 2173 spin_lock(&tty_files_lock);
2174 if (!list_empty(&tty->tty_files)) { 2174 if (!list_empty(&tty->tty_files)) {
2175 struct tty_file_private *file_priv;
2175 struct inode *inode; 2176 struct inode *inode;
2176 2177
2177 /* Revalidate access to controlling tty. 2178 /* Revalidate access to controlling tty.
@@ -2179,7 +2180,9 @@ static inline void flush_unauthorized_files(const struct cred *cred,
2179 than using file_has_perm, as this particular open 2180 than using file_has_perm, as this particular open
2180 file may belong to another process and we are only 2181 file may belong to another process and we are only
2181 interested in the inode-based check here. */ 2182 interested in the inode-based check here. */
2182 file = list_first_entry(&tty->tty_files, struct file, f_u.fu_list); 2183 file_priv = list_first_entry(&tty->tty_files,
2184 struct tty_file_private, list);
2185 file = file_priv->file;
2183 inode = file->f_path.dentry->d_inode; 2186 inode = file->f_path.dentry->d_inode;
2184 if (inode_has_perm(cred, inode, 2187 if (inode_has_perm(cred, inode,
2185 FILE__READ | FILE__WRITE, NULL)) { 2188 FILE__READ | FILE__WRITE, NULL)) {