aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/char/tty_io.c
diff options
context:
space:
mode:
authorAlan Cox <alan@redhat.com>2007-07-16 02:39:43 -0400
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-07-16 12:05:41 -0400
commit9c1729db3e6d738f872bcb090212af00473bf666 (patch)
treebe68b99784607953fb50d9330d34c2728215be57 /drivers/char/tty_io.c
parentc9c64155f5a81b4b41e98f9fb9c464a565c1bf72 (diff)
Prevent an O_NDELAY writer from blocking when a tty write is blocked by the tty atomic writer mutex
Without this a tty write could block if a previous blocking tty write was in progress on the same tty and blocked by a line discipline or hardware event. Originally found and reported by Dave Johnson. Signed-off-by: Alan Cox <alan@redhat.com> Acked-by: Dave Johnson <djohnson+linux-kernel@sw.starentnetworks.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/char/tty_io.c')
-rw-r--r--drivers/char/tty_io.c40
1 files changed, 28 insertions, 12 deletions
diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c
index a96f26a63fa2..a37e6330db8a 100644
--- a/drivers/char/tty_io.c
+++ b/drivers/char/tty_io.c
@@ -1726,6 +1726,23 @@ static ssize_t tty_read(struct file * file, char __user * buf, size_t count,
1726 return i; 1726 return i;
1727} 1727}
1728 1728
1729void tty_write_unlock(struct tty_struct *tty)
1730{
1731 mutex_unlock(&tty->atomic_write_lock);
1732 wake_up_interruptible(&tty->write_wait);
1733}
1734
1735int tty_write_lock(struct tty_struct *tty, int ndelay)
1736{
1737 if (!mutex_trylock(&tty->atomic_write_lock)) {
1738 if (ndelay)
1739 return -EAGAIN;
1740 if (mutex_lock_interruptible(&tty->atomic_write_lock))
1741 return -ERESTARTSYS;
1742 }
1743 return 0;
1744}
1745
1729/* 1746/*
1730 * Split writes up in sane blocksizes to avoid 1747 * Split writes up in sane blocksizes to avoid
1731 * denial-of-service type attacks 1748 * denial-of-service type attacks
@@ -1737,13 +1754,12 @@ static inline ssize_t do_tty_write(
1737 const char __user *buf, 1754 const char __user *buf,
1738 size_t count) 1755 size_t count)
1739{ 1756{
1740 ssize_t ret = 0, written = 0; 1757 ssize_t ret, written = 0;
1741 unsigned int chunk; 1758 unsigned int chunk;
1742 1759
1743 /* FIXME: O_NDELAY ... */ 1760 ret = tty_write_lock(tty, file->f_flags & O_NDELAY);
1744 if (mutex_lock_interruptible(&tty->atomic_write_lock)) { 1761 if (ret < 0)
1745 return -ERESTARTSYS; 1762 return ret;
1746 }
1747 1763
1748 /* 1764 /*
1749 * We chunk up writes into a temporary buffer. This 1765 * We chunk up writes into a temporary buffer. This
@@ -1776,8 +1792,8 @@ static inline ssize_t do_tty_write(
1776 1792
1777 buf = kmalloc(chunk, GFP_KERNEL); 1793 buf = kmalloc(chunk, GFP_KERNEL);
1778 if (!buf) { 1794 if (!buf) {
1779 mutex_unlock(&tty->atomic_write_lock); 1795 ret = -ENOMEM;
1780 return -ENOMEM; 1796 goto out;
1781 } 1797 }
1782 kfree(tty->write_buf); 1798 kfree(tty->write_buf);
1783 tty->write_cnt = chunk; 1799 tty->write_cnt = chunk;
@@ -1812,7 +1828,8 @@ static inline ssize_t do_tty_write(
1812 inode->i_mtime = current_fs_time(inode->i_sb); 1828 inode->i_mtime = current_fs_time(inode->i_sb);
1813 ret = written; 1829 ret = written;
1814 } 1830 }
1815 mutex_unlock(&tty->atomic_write_lock); 1831out:
1832 tty_write_unlock(tty);
1816 return ret; 1833 return ret;
1817} 1834}
1818 1835
@@ -3163,14 +3180,13 @@ static int tiocsetd(struct tty_struct *tty, int __user *p)
3163 3180
3164static int send_break(struct tty_struct *tty, unsigned int duration) 3181static int send_break(struct tty_struct *tty, unsigned int duration)
3165{ 3182{
3166 if (mutex_lock_interruptible(&tty->atomic_write_lock)) 3183 if (tty_write_lock(tty, 0) < 0)
3167 return -EINTR; 3184 return -EINTR;
3168 tty->driver->break_ctl(tty, -1); 3185 tty->driver->break_ctl(tty, -1);
3169 if (!signal_pending(current)) { 3186 if (!signal_pending(current))
3170 msleep_interruptible(duration); 3187 msleep_interruptible(duration);
3171 }
3172 tty->driver->break_ctl(tty, 0); 3188 tty->driver->break_ctl(tty, 0);
3173 mutex_unlock(&tty->atomic_write_lock); 3189 tty_write_unlock(tty);
3174 if (signal_pending(current)) 3190 if (signal_pending(current))
3175 return -EINTR; 3191 return -EINTR;
3176 return 0; 3192 return 0;