aboutsummaryrefslogtreecommitdiffstats
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
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>
-rw-r--r--drivers/char/n_hdlc.c9
-rw-r--r--drivers/char/n_tty.c3
-rw-r--r--drivers/char/tty_io.c40
-rw-r--r--drivers/char/tty_ioctl.c4
-rw-r--r--include/linux/tty.h6
5 files changed, 43 insertions, 19 deletions
diff --git a/drivers/char/n_hdlc.c b/drivers/char/n_hdlc.c
index 337a87f86a3b..37f7d3403040 100644
--- a/drivers/char/n_hdlc.c
+++ b/drivers/char/n_hdlc.c
@@ -780,13 +780,14 @@ static unsigned int n_hdlc_tty_poll(struct tty_struct *tty, struct file *filp,
780 poll_wait(filp, &tty->write_wait, wait); 780 poll_wait(filp, &tty->write_wait, wait);
781 781
782 /* set bits for operations that won't block */ 782 /* set bits for operations that won't block */
783 if(n_hdlc->rx_buf_list.head) 783 if (n_hdlc->rx_buf_list.head)
784 mask |= POLLIN | POLLRDNORM; /* readable */ 784 mask |= POLLIN | POLLRDNORM; /* readable */
785 if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) 785 if (test_bit(TTY_OTHER_CLOSED, &tty->flags))
786 mask |= POLLHUP; 786 mask |= POLLHUP;
787 if(tty_hung_up_p(filp)) 787 if (tty_hung_up_p(filp))
788 mask |= POLLHUP; 788 mask |= POLLHUP;
789 if(n_hdlc->tx_free_buf_list.head) 789 if (!tty_is_writelocked(tty) &&
790 n_hdlc->tx_free_buf_list.head)
790 mask |= POLLOUT | POLLWRNORM; /* writable */ 791 mask |= POLLOUT | POLLWRNORM; /* writable */
791 } 792 }
792 return mask; 793 return mask;
@@ -861,7 +862,7 @@ static void n_hdlc_buf_put(struct n_hdlc_buf_list *list,
861 spin_lock_irqsave(&list->spinlock,flags); 862 spin_lock_irqsave(&list->spinlock,flags);
862 863
863 buf->link=NULL; 864 buf->link=NULL;
864 if(list->tail) 865 if (list->tail)
865 list->tail->link = buf; 866 list->tail->link = buf;
866 else 867 else
867 list->head = buf; 868 list->head = buf;
diff --git a/drivers/char/n_tty.c b/drivers/char/n_tty.c
index 154f42203b05..371631f4bfb9 100644
--- a/drivers/char/n_tty.c
+++ b/drivers/char/n_tty.c
@@ -1538,7 +1538,8 @@ static unsigned int normal_poll(struct tty_struct * tty, struct file * file, pol
1538 else 1538 else
1539 tty->minimum_to_wake = 1; 1539 tty->minimum_to_wake = 1;
1540 } 1540 }
1541 if (tty->driver->chars_in_buffer(tty) < WAKEUP_CHARS && 1541 if (!tty_is_writelocked(tty) &&
1542 tty->driver->chars_in_buffer(tty) < WAKEUP_CHARS &&
1542 tty->driver->write_room(tty) > 0) 1543 tty->driver->write_room(tty) > 0)
1543 mask |= POLLOUT | POLLWRNORM; 1544 mask |= POLLOUT | POLLWRNORM;
1544 return mask; 1545 return mask;
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;
diff --git a/drivers/char/tty_ioctl.c b/drivers/char/tty_ioctl.c
index fd471cb3338f..918e24c885f1 100644
--- a/drivers/char/tty_ioctl.c
+++ b/drivers/char/tty_ioctl.c
@@ -667,7 +667,7 @@ static int send_prio_char(struct tty_struct *tty, char ch)
667 return 0; 667 return 0;
668 } 668 }
669 669
670 if (mutex_lock_interruptible(&tty->atomic_write_lock)) 670 if (tty_write_lock(tty, 0) < 0)
671 return -ERESTARTSYS; 671 return -ERESTARTSYS;
672 672
673 if (was_stopped) 673 if (was_stopped)
@@ -675,7 +675,7 @@ static int send_prio_char(struct tty_struct *tty, char ch)
675 tty->driver->write(tty, &ch, 1); 675 tty->driver->write(tty, &ch, 1);
676 if (was_stopped) 676 if (was_stopped)
677 stop_tty(tty); 677 stop_tty(tty);
678 mutex_unlock(&tty->atomic_write_lock); 678 tty_write_unlock(tty);
679 return 0; 679 return 0;
680} 680}
681 681
diff --git a/include/linux/tty.h b/include/linux/tty.h
index bb4576085203..deaba9ec5004 100644
--- a/include/linux/tty.h
+++ b/include/linux/tty.h
@@ -338,6 +338,12 @@ extern struct tty_struct *get_current_tty(void);
338 338
339extern struct mutex tty_mutex; 339extern struct mutex tty_mutex;
340 340
341extern void tty_write_unlock(struct tty_struct *tty);
342extern int tty_write_lock(struct tty_struct *tty, int ndelay);
343#define tty_is_writelocked(tty) (mutex_is_locked(&tty->atomic_write_lock))
344
345
346
341/* n_tty.c */ 347/* n_tty.c */
342extern struct tty_ldisc tty_ldisc_N_TTY; 348extern struct tty_ldisc tty_ldisc_N_TTY;
343 349