diff options
author | Alan Cox <alan@redhat.com> | 2007-07-16 02:39:43 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-07-16 12:05:41 -0400 |
commit | 9c1729db3e6d738f872bcb090212af00473bf666 (patch) | |
tree | be68b99784607953fb50d9330d34c2728215be57 /drivers/char/tty_io.c | |
parent | c9c64155f5a81b4b41e98f9fb9c464a565c1bf72 (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.c | 40 |
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 | ||
1729 | void tty_write_unlock(struct tty_struct *tty) | ||
1730 | { | ||
1731 | mutex_unlock(&tty->atomic_write_lock); | ||
1732 | wake_up_interruptible(&tty->write_wait); | ||
1733 | } | ||
1734 | |||
1735 | int 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); | 1831 | out: |
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 | ||
3164 | static int send_break(struct tty_struct *tty, unsigned int duration) | 3181 | static 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; |