aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/tty
diff options
context:
space:
mode:
authorJiri Slaby <jslaby@suse.cz>2011-10-12 05:32:43 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2011-10-18 17:22:37 -0400
commitfa90e1c935472281de314e6d7c9a37db9cbc2e4e (patch)
tree2c89c0038c640b6bdb5caaef00401f22553b118a /drivers/tty
parentc290f8358acaeffd8e0c551ddcc24d1206143376 (diff)
TTY: make tty_add_file non-failing
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: stable <stable@vger.kernel.org> Cc: Alan Cox <alan@lxorguk.ukuu.org.uk> Cc: Pekka Enberg <penberg@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/tty')
-rw-r--r--drivers/tty/pty.c16
-rw-r--r--drivers/tty/tty_io.c47
2 files changed, 46 insertions, 17 deletions
diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c
index 98b6e3bdb000..7613f95f2d6b 100644
--- a/drivers/tty/pty.c
+++ b/drivers/tty/pty.c
@@ -657,12 +657,18 @@ static int ptmx_open(struct inode *inode, struct file *filp)
657 657
658 nonseekable_open(inode, filp); 658 nonseekable_open(inode, filp);
659 659
660 retval = tty_alloc_file(filp);
661 if (retval)
662 return retval;
663
660 /* find a device that is not in use. */ 664 /* find a device that is not in use. */
661 tty_lock(); 665 tty_lock();
662 index = devpts_new_index(inode); 666 index = devpts_new_index(inode);
663 tty_unlock(); 667 tty_unlock();
664 if (index < 0) 668 if (index < 0) {
665 return index; 669 retval = index;
670 goto err_file;
671 }
666 672
667 mutex_lock(&tty_mutex); 673 mutex_lock(&tty_mutex);
668 tty_lock(); 674 tty_lock();
@@ -676,9 +682,7 @@ static int ptmx_open(struct inode *inode, struct file *filp)
676 682
677 set_bit(TTY_PTY_LOCK, &tty->flags); /* LOCK THE SLAVE */ 683 set_bit(TTY_PTY_LOCK, &tty->flags); /* LOCK THE SLAVE */
678 684
679 retval = tty_add_file(tty, filp); 685 tty_add_file(tty, filp);
680 if (retval)
681 goto out;
682 686
683 retval = devpts_pty_new(inode, tty->link); 687 retval = devpts_pty_new(inode, tty->link);
684 if (retval) 688 if (retval)
@@ -697,6 +701,8 @@ out2:
697out: 701out:
698 devpts_kill_index(inode, index); 702 devpts_kill_index(inode, index);
699 tty_unlock(); 703 tty_unlock();
704err_file:
705 tty_free_file(filp);
700 return retval; 706 return retval;
701} 707}
702 708
diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c
index 6913da8f202c..767ecbb4761a 100644
--- a/drivers/tty/tty_io.c
+++ b/drivers/tty/tty_io.c
@@ -194,8 +194,7 @@ static inline struct tty_struct *file_tty(struct file *file)
194 return ((struct tty_file_private *)file->private_data)->tty; 194 return ((struct tty_file_private *)file->private_data)->tty;
195} 195}
196 196
197/* Associate a new file with the tty structure */ 197int tty_alloc_file(struct file *file)
198int tty_add_file(struct tty_struct *tty, struct file *file)
199{ 198{
200 struct tty_file_private *priv; 199 struct tty_file_private *priv;
201 200
@@ -203,15 +202,36 @@ int tty_add_file(struct tty_struct *tty, struct file *file)
203 if (!priv) 202 if (!priv)
204 return -ENOMEM; 203 return -ENOMEM;
205 204
205 file->private_data = priv;
206
207 return 0;
208}
209
210/* Associate a new file with the tty structure */
211void tty_add_file(struct tty_struct *tty, struct file *file)
212{
213 struct tty_file_private *priv = file->private_data;
214
206 priv->tty = tty; 215 priv->tty = tty;
207 priv->file = file; 216 priv->file = file;
208 file->private_data = priv;
209 217
210 spin_lock(&tty_files_lock); 218 spin_lock(&tty_files_lock);
211 list_add(&priv->list, &tty->tty_files); 219 list_add(&priv->list, &tty->tty_files);
212 spin_unlock(&tty_files_lock); 220 spin_unlock(&tty_files_lock);
221}
213 222
214 return 0; 223/**
224 * tty_free_file - free file->private_data
225 *
226 * This shall be used only for fail path handling when tty_add_file was not
227 * called yet.
228 */
229void tty_free_file(struct file *file)
230{
231 struct tty_file_private *priv = file->private_data;
232
233 file->private_data = NULL;
234 kfree(priv);
215} 235}
216 236
217/* Delete file from its tty */ 237/* Delete file from its tty */
@@ -222,8 +242,7 @@ void tty_del_file(struct file *file)
222 spin_lock(&tty_files_lock); 242 spin_lock(&tty_files_lock);
223 list_del(&priv->list); 243 list_del(&priv->list);
224 spin_unlock(&tty_files_lock); 244 spin_unlock(&tty_files_lock);
225 file->private_data = NULL; 245 tty_free_file(file);
226 kfree(priv);
227} 246}
228 247
229 248
@@ -1812,6 +1831,10 @@ static int tty_open(struct inode *inode, struct file *filp)
1812 nonseekable_open(inode, filp); 1831 nonseekable_open(inode, filp);
1813 1832
1814retry_open: 1833retry_open:
1834 retval = tty_alloc_file(filp);
1835 if (retval)
1836 return -ENOMEM;
1837
1815 noctty = filp->f_flags & O_NOCTTY; 1838 noctty = filp->f_flags & O_NOCTTY;
1816 index = -1; 1839 index = -1;
1817 retval = 0; 1840 retval = 0;
@@ -1824,6 +1847,7 @@ retry_open:
1824 if (!tty) { 1847 if (!tty) {
1825 tty_unlock(); 1848 tty_unlock();
1826 mutex_unlock(&tty_mutex); 1849 mutex_unlock(&tty_mutex);
1850 tty_free_file(filp);
1827 return -ENXIO; 1851 return -ENXIO;
1828 } 1852 }
1829 driver = tty_driver_kref_get(tty->driver); 1853 driver = tty_driver_kref_get(tty->driver);
@@ -1856,6 +1880,7 @@ retry_open:
1856 } 1880 }
1857 tty_unlock(); 1881 tty_unlock();
1858 mutex_unlock(&tty_mutex); 1882 mutex_unlock(&tty_mutex);
1883 tty_free_file(filp);
1859 return -ENODEV; 1884 return -ENODEV;
1860 } 1885 }
1861 1886
@@ -1863,6 +1888,7 @@ retry_open:
1863 if (!driver) { 1888 if (!driver) {
1864 tty_unlock(); 1889 tty_unlock();
1865 mutex_unlock(&tty_mutex); 1890 mutex_unlock(&tty_mutex);
1891 tty_free_file(filp);
1866 return -ENODEV; 1892 return -ENODEV;
1867 } 1893 }
1868got_driver: 1894got_driver:
@@ -1874,6 +1900,7 @@ got_driver:
1874 tty_unlock(); 1900 tty_unlock();
1875 mutex_unlock(&tty_mutex); 1901 mutex_unlock(&tty_mutex);
1876 tty_driver_kref_put(driver); 1902 tty_driver_kref_put(driver);
1903 tty_free_file(filp);
1877 return PTR_ERR(tty); 1904 return PTR_ERR(tty);
1878 } 1905 }
1879 } 1906 }
@@ -1889,15 +1916,11 @@ got_driver:
1889 tty_driver_kref_put(driver); 1916 tty_driver_kref_put(driver);
1890 if (IS_ERR(tty)) { 1917 if (IS_ERR(tty)) {
1891 tty_unlock(); 1918 tty_unlock();
1919 tty_free_file(filp);
1892 return PTR_ERR(tty); 1920 return PTR_ERR(tty);
1893 } 1921 }
1894 1922
1895 retval = tty_add_file(tty, filp); 1923 tty_add_file(tty, filp);
1896 if (retval) {
1897 tty_unlock();
1898 tty_release(inode, filp);
1899 return retval;
1900 }
1901 1924
1902 check_tty_count(tty, "tty_open"); 1925 check_tty_count(tty, "tty_open");
1903 if (tty->driver->type == TTY_DRIVER_TYPE_PTY && 1926 if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&