Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Jiri Slaby and committed by
Greg Kroah-Hartman
fa90e1c9 c290f835

+49 -18
+11 -5
drivers/tty/pty.c
··· 657 657 658 658 nonseekable_open(inode, filp); 659 659 660 + retval = tty_alloc_file(filp); 661 + if (retval) 662 + return retval; 663 + 660 664 /* find a device that is not in use. */ 661 665 tty_lock(); 662 666 index = devpts_new_index(inode); 663 667 tty_unlock(); 664 - if (index < 0) 665 - return index; 668 + if (index < 0) { 669 + retval = index; 670 + goto err_file; 671 + } 666 672 667 673 mutex_lock(&tty_mutex); 668 674 tty_lock(); ··· 682 676 683 677 set_bit(TTY_PTY_LOCK, &tty->flags); /* LOCK THE SLAVE */ 684 678 685 - retval = tty_add_file(tty, filp); 686 - if (retval) 687 - goto out; 679 + tty_add_file(tty, filp); 688 680 689 681 retval = devpts_pty_new(inode, tty->link); 690 682 if (retval) ··· 701 697 out: 702 698 devpts_kill_index(inode, index); 703 699 tty_unlock(); 700 + err_file: 701 + tty_free_file(filp); 704 702 return retval; 705 703 } 706 704
+35 -12
drivers/tty/tty_io.c
··· 194 194 return ((struct tty_file_private *)file->private_data)->tty; 195 195 } 196 196 197 - /* Associate a new file with the tty structure */ 198 - int tty_add_file(struct tty_struct *tty, struct file *file) 197 + int tty_alloc_file(struct file *file) 199 198 { 200 199 struct tty_file_private *priv; 201 200 ··· 202 203 if (!priv) 203 204 return -ENOMEM; 204 205 206 + file->private_data = priv; 207 + 208 + return 0; 209 + } 210 + 211 + /* Associate a new file with the tty structure */ 212 + void tty_add_file(struct tty_struct *tty, struct file *file) 213 + { 214 + struct tty_file_private *priv = file->private_data; 215 + 205 216 priv->tty = tty; 206 217 priv->file = file; 207 - file->private_data = priv; 208 218 209 219 spin_lock(&tty_files_lock); 210 220 list_add(&priv->list, &tty->tty_files); 211 221 spin_unlock(&tty_files_lock); 222 + } 212 223 213 - return 0; 224 + /** 225 + * tty_free_file - free file->private_data 226 + * 227 + * This shall be used only for fail path handling when tty_add_file was not 228 + * called yet. 229 + */ 230 + void tty_free_file(struct file *file) 231 + { 232 + struct tty_file_private *priv = file->private_data; 233 + 234 + file->private_data = NULL; 235 + kfree(priv); 214 236 } 215 237 216 238 /* Delete file from its tty */ ··· 242 222 spin_lock(&tty_files_lock); 243 223 list_del(&priv->list); 244 224 spin_unlock(&tty_files_lock); 245 - file->private_data = NULL; 246 - kfree(priv); 225 + tty_free_file(file); 247 226 } 248 227 249 228 ··· 1831 1812 nonseekable_open(inode, filp); 1832 1813 1833 1814 retry_open: 1815 + retval = tty_alloc_file(filp); 1816 + if (retval) 1817 + return -ENOMEM; 1818 + 1834 1819 noctty = filp->f_flags & O_NOCTTY; 1835 1820 index = -1; 1836 1821 retval = 0; ··· 1847 1824 if (!tty) { 1848 1825 tty_unlock(); 1849 1826 mutex_unlock(&tty_mutex); 1827 + tty_free_file(filp); 1850 1828 return -ENXIO; 1851 1829 } 1852 1830 driver = tty_driver_kref_get(tty->driver); ··· 1880 1856 } 1881 1857 tty_unlock(); 1882 1858 mutex_unlock(&tty_mutex); 1859 + tty_free_file(filp); 1883 1860 return -ENODEV; 1884 1861 } 1885 1862 ··· 1888 1863 if (!driver) { 1889 1864 tty_unlock(); 1890 1865 mutex_unlock(&tty_mutex); 1866 + tty_free_file(filp); 1891 1867 return -ENODEV; 1892 1868 } 1893 1869 got_driver: ··· 1900 1874 tty_unlock(); 1901 1875 mutex_unlock(&tty_mutex); 1902 1876 tty_driver_kref_put(driver); 1877 + tty_free_file(filp); 1903 1878 return PTR_ERR(tty); 1904 1879 } 1905 1880 } ··· 1916 1889 tty_driver_kref_put(driver); 1917 1890 if (IS_ERR(tty)) { 1918 1891 tty_unlock(); 1892 + tty_free_file(filp); 1919 1893 return PTR_ERR(tty); 1920 1894 } 1921 1895 1922 - retval = tty_add_file(tty, filp); 1923 - if (retval) { 1924 - tty_unlock(); 1925 - tty_release(inode, filp); 1926 - return retval; 1927 - } 1896 + tty_add_file(tty, filp); 1928 1897 1929 1898 check_tty_count(tty, "tty_open"); 1930 1899 if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
+3 -1
include/linux/tty.h
··· 471 471 extern struct tty_struct *get_current_tty(void); 472 472 extern void tty_default_fops(struct file_operations *fops); 473 473 extern struct tty_struct *alloc_tty_struct(void); 474 - extern int tty_add_file(struct tty_struct *tty, struct file *file); 474 + extern int tty_alloc_file(struct file *file); 475 + extern void tty_add_file(struct tty_struct *tty, struct file *file); 476 + extern void tty_free_file(struct file *file); 475 477 extern void free_tty_struct(struct tty_struct *tty); 476 478 extern void initialize_tty_struct(struct tty_struct *tty, 477 479 struct tty_driver *driver, int idx);