aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJiri Slaby <jslaby@suse.cz>2011-10-12 05:32:43 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2011-11-11 12:35:12 -0500
commit0c1f111ae7fcea822fd1c078ef48e88d93afc57a (patch)
tree5816e47f84c99a1378563bc88b7503f974cf1f61
parent36174dd629350d0654982977d7795ca28475c16f (diff)
TTY: make tty_add_file non-failing
commit fa90e1c935472281de314e6d7c9a37db9cbc2e4e upstream. If tty_add_file fails at the point it is now, we have to revert all the changes we did to the tty. It means either decrease all refcounts if this was a tty reopen or delete the tty if it was newly allocated. There was a try to fix this in v3.0-rc2 using tty_release in 0259894c7 (TTY: fix fail path in tty_open). But instead it introduced a NULL dereference. It's because tty_release dereferences filp->private_data, but that one is set even in our tty_add_file. And when tty_add_file fails, it's still NULL/garbage. Hence tty_release cannot be called there. To circumvent the original leak (and the current NULL deref) we split tty_add_file into two functions, making the latter non-failing. In that case we may do the former early in open, where handling failures is easy. The latter stays as it is now. So there is no change in functionality. The original bug (leak) was introduced by f573bd176 (tty: Remove __GFP_NOFAIL from tty_add_file()). Thanks Dan for reporting this. Later, we may split tty_release into more functions and call only some of them in this fail path instead. (If at all possible.) Introduced-in: v2.6.37-rc2 Signed-off-by: Jiri Slaby <jslaby@suse.cz> Reported-by: Dan Carpenter <dan.carpenter@oracle.com> Cc: Alan Cox <alan@lxorguk.ukuu.org.uk> Cc: Pekka Enberg <penberg@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r--drivers/tty/pty.c16
-rw-r--r--drivers/tty/tty_io.c47
-rw-r--r--include/linux/tty.h4
3 files changed, 49 insertions, 18 deletions
diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c
index e809e9d4683..696e8510a5f 100644
--- a/drivers/tty/pty.c
+++ b/drivers/tty/pty.c
@@ -670,12 +670,18 @@ static int ptmx_open(struct inode *inode, struct file *filp)
670 670
671 nonseekable_open(inode, filp); 671 nonseekable_open(inode, filp);
672 672
673 retval = tty_alloc_file(filp);
674 if (retval)
675 return retval;
676
673 /* find a device that is not in use. */ 677 /* find a device that is not in use. */
674 tty_lock(); 678 tty_lock();
675 index = devpts_new_index(inode); 679 index = devpts_new_index(inode);
676 tty_unlock(); 680 tty_unlock();
677 if (index < 0) 681 if (index < 0) {
678 return index; 682 retval = index;
683 goto err_file;
684 }
679 685
680 mutex_lock(&tty_mutex); 686 mutex_lock(&tty_mutex);
681 tty_lock(); 687 tty_lock();
@@ -689,9 +695,7 @@ static int ptmx_open(struct inode *inode, struct file *filp)
689 695
690 set_bit(TTY_PTY_LOCK, &tty->flags); /* LOCK THE SLAVE */ 696 set_bit(TTY_PTY_LOCK, &tty->flags); /* LOCK THE SLAVE */
691 697
692 retval = tty_add_file(tty, filp); 698 tty_add_file(tty, filp);
693 if (retval)
694 goto out;
695 699
696 retval = devpts_pty_new(inode, tty->link); 700 retval = devpts_pty_new(inode, tty->link);
697 if (retval) 701 if (retval)
@@ -710,6 +714,8 @@ out2:
710out: 714out:
711 devpts_kill_index(inode, index); 715 devpts_kill_index(inode, index);
712 tty_unlock(); 716 tty_unlock();
717err_file:
718 tty_free_file(filp);
713 return retval; 719 return retval;
714} 720}
715 721
diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c
index e9495ed8ec0..b44aef078f1 100644
--- a/drivers/tty/tty_io.c
+++ b/drivers/tty/tty_io.c
@@ -193,8 +193,7 @@ static inline struct tty_struct *file_tty(struct file *file)
193 return ((struct tty_file_private *)file->private_data)->tty; 193 return ((struct tty_file_private *)file->private_data)->tty;
194} 194}
195 195
196/* Associate a new file with the tty structure */ 196int tty_alloc_file(struct file *file)
197int tty_add_file(struct tty_struct *tty, struct file *file)
198{ 197{
199 struct tty_file_private *priv; 198 struct tty_file_private *priv;
200 199
@@ -202,15 +201,36 @@ int tty_add_file(struct tty_struct *tty, struct file *file)
202 if (!priv) 201 if (!priv)
203 return -ENOMEM; 202 return -ENOMEM;
204 203
204 file->private_data = priv;
205
206 return 0;
207}
208
209/* Associate a new file with the tty structure */
210void tty_add_file(struct tty_struct *tty, struct file *file)
211{
212 struct tty_file_private *priv = file->private_data;
213
205 priv->tty = tty; 214 priv->tty = tty;
206 priv->file = file; 215 priv->file = file;
207 file->private_data = priv;
208 216
209 spin_lock(&tty_files_lock); 217 spin_lock(&tty_files_lock);
210 list_add(&priv->list, &tty->tty_files); 218 list_add(&priv->list, &tty->tty_files);
211 spin_unlock(&tty_files_lock); 219 spin_unlock(&tty_files_lock);
220}
212 221
213 return 0; 222/**
223 * tty_free_file - free file->private_data
224 *
225 * This shall be used only for fail path handling when tty_add_file was not
226 * called yet.
227 */
228void tty_free_file(struct file *file)
229{
230 struct tty_file_private *priv = file->private_data;
231
232 file->private_data = NULL;
233 kfree(priv);
214} 234}
215 235
216/* Delete file from its tty */ 236/* Delete file from its tty */
@@ -221,8 +241,7 @@ void tty_del_file(struct file *file)
221 spin_lock(&tty_files_lock); 241 spin_lock(&tty_files_lock);
222 list_del(&priv->list); 242 list_del(&priv->list);
223 spin_unlock(&tty_files_lock); 243 spin_unlock(&tty_files_lock);
224 file->private_data = NULL; 244 tty_free_file(file);
225 kfree(priv);
226} 245}
227 246
228 247
@@ -1811,6 +1830,10 @@ static int tty_open(struct inode *inode, struct file *filp)
1811 nonseekable_open(inode, filp); 1830 nonseekable_open(inode, filp);
1812 1831
1813retry_open: 1832retry_open:
1833 retval = tty_alloc_file(filp);
1834 if (retval)
1835 return -ENOMEM;
1836
1814 noctty = filp->f_flags & O_NOCTTY; 1837 noctty = filp->f_flags & O_NOCTTY;
1815 index = -1; 1838 index = -1;
1816 retval = 0; 1839 retval = 0;
@@ -1823,6 +1846,7 @@ retry_open:
1823 if (!tty) { 1846 if (!tty) {
1824 tty_unlock(); 1847 tty_unlock();
1825 mutex_unlock(&tty_mutex); 1848 mutex_unlock(&tty_mutex);
1849 tty_free_file(filp);
1826 return -ENXIO; 1850 return -ENXIO;
1827 } 1851 }
1828 driver = tty_driver_kref_get(tty->driver); 1852 driver = tty_driver_kref_get(tty->driver);
@@ -1855,6 +1879,7 @@ retry_open:
1855 } 1879 }
1856 tty_unlock(); 1880 tty_unlock();
1857 mutex_unlock(&tty_mutex); 1881 mutex_unlock(&tty_mutex);
1882 tty_free_file(filp);
1858 return -ENODEV; 1883 return -ENODEV;
1859 } 1884 }
1860 1885
@@ -1862,6 +1887,7 @@ retry_open:
1862 if (!driver) { 1887 if (!driver) {
1863 tty_unlock(); 1888 tty_unlock();
1864 mutex_unlock(&tty_mutex); 1889 mutex_unlock(&tty_mutex);
1890 tty_free_file(filp);
1865 return -ENODEV; 1891 return -ENODEV;
1866 } 1892 }
1867got_driver: 1893got_driver:
@@ -1873,6 +1899,7 @@ got_driver:
1873 tty_unlock(); 1899 tty_unlock();
1874 mutex_unlock(&tty_mutex); 1900 mutex_unlock(&tty_mutex);
1875 tty_driver_kref_put(driver); 1901 tty_driver_kref_put(driver);
1902 tty_free_file(filp);
1876 return PTR_ERR(tty); 1903 return PTR_ERR(tty);
1877 } 1904 }
1878 } 1905 }
@@ -1888,15 +1915,11 @@ got_driver:
1888 tty_driver_kref_put(driver); 1915 tty_driver_kref_put(driver);
1889 if (IS_ERR(tty)) { 1916 if (IS_ERR(tty)) {
1890 tty_unlock(); 1917 tty_unlock();
1918 tty_free_file(filp);
1891 return PTR_ERR(tty); 1919 return PTR_ERR(tty);
1892 } 1920 }
1893 1921
1894 retval = tty_add_file(tty, filp); 1922 tty_add_file(tty, filp);
1895 if (retval) {
1896 tty_unlock();
1897 tty_release(inode, filp);
1898 return retval;
1899 }
1900 1923
1901 check_tty_count(tty, "tty_open"); 1924 check_tty_count(tty, "tty_open");
1902 if (tty->driver->type == TTY_DRIVER_TYPE_PTY && 1925 if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
diff --git a/include/linux/tty.h b/include/linux/tty.h
index 6660c41949b..1ff6b62fb69 100644
--- a/include/linux/tty.h
+++ b/include/linux/tty.h
@@ -472,7 +472,9 @@ extern void proc_clear_tty(struct task_struct *p);
472extern struct tty_struct *get_current_tty(void); 472extern struct tty_struct *get_current_tty(void);
473extern void tty_default_fops(struct file_operations *fops); 473extern void tty_default_fops(struct file_operations *fops);
474extern struct tty_struct *alloc_tty_struct(void); 474extern struct tty_struct *alloc_tty_struct(void);
475extern int tty_add_file(struct tty_struct *tty, struct file *file); 475extern int tty_alloc_file(struct file *file);
476extern void tty_add_file(struct tty_struct *tty, struct file *file);
477extern void tty_free_file(struct file *file);
476extern void free_tty_struct(struct tty_struct *tty); 478extern void free_tty_struct(struct tty_struct *tty);
477extern void initialize_tty_struct(struct tty_struct *tty, 479extern void initialize_tty_struct(struct tty_struct *tty,
478 struct tty_driver *driver, int idx); 480 struct tty_driver *driver, int idx);