diff options
author | Alan Cox <alan@lxorguk.ukuu.org.uk> | 2008-04-30 03:53:30 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-04-30 11:29:40 -0400 |
commit | 47f86834bbd4193139d61d659bebf9ab9d691e37 (patch) | |
tree | 6724b07e24929eba5c6df31f07871d9d6b4aa296 | |
parent | 04f378b198da233ca0aca341b113dc6579d46123 (diff) |
redo locking of tty->pgrp
Historically tty->pgrp and friends were pid_t and the code "knew" they were
safe. The change to pid structs opened up a few races and the removal of the
BKL in places made them quite hittable. We put tty->pgrp under the ctrl_lock
for the tty.
Signed-off-by: Alan Cox <alan@redhat.com>
Cc: Oleg Nesterov <oleg@tv-sign.ru>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | drivers/char/tty_io.c | 78 | ||||
-rw-r--r-- | drivers/char/vt.c | 6 | ||||
-rw-r--r-- | include/linux/tty.h | 10 |
3 files changed, 72 insertions, 22 deletions
diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index 0b0354bc28d6..c8aa318eaa18 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c | |||
@@ -1204,26 +1204,37 @@ EXPORT_SYMBOL_GPL(tty_find_polling_driver); | |||
1204 | * not in the foreground, send a SIGTTOU. If the signal is blocked or | 1204 | * not in the foreground, send a SIGTTOU. If the signal is blocked or |
1205 | * ignored, go ahead and perform the operation. (POSIX 7.2) | 1205 | * ignored, go ahead and perform the operation. (POSIX 7.2) |
1206 | * | 1206 | * |
1207 | * Locking: none - FIXME: review this | 1207 | * Locking: ctrl_lock - FIXME: review this |
1208 | */ | 1208 | */ |
1209 | 1209 | ||
1210 | int tty_check_change(struct tty_struct *tty) | 1210 | int tty_check_change(struct tty_struct *tty) |
1211 | { | 1211 | { |
1212 | unsigned long flags; | ||
1213 | int ret = 0; | ||
1214 | |||
1212 | if (current->signal->tty != tty) | 1215 | if (current->signal->tty != tty) |
1213 | return 0; | 1216 | return 0; |
1217 | |||
1218 | spin_lock_irqsave(&tty->ctrl_lock, flags); | ||
1219 | |||
1214 | if (!tty->pgrp) { | 1220 | if (!tty->pgrp) { |
1215 | printk(KERN_WARNING "tty_check_change: tty->pgrp == NULL!\n"); | 1221 | printk(KERN_WARNING "tty_check_change: tty->pgrp == NULL!\n"); |
1216 | return 0; | 1222 | goto out; |
1217 | } | 1223 | } |
1218 | if (task_pgrp(current) == tty->pgrp) | 1224 | if (task_pgrp(current) == tty->pgrp) |
1219 | return 0; | 1225 | goto out; |
1220 | if (is_ignored(SIGTTOU)) | 1226 | if (is_ignored(SIGTTOU)) |
1221 | return 0; | 1227 | goto out; |
1222 | if (is_current_pgrp_orphaned()) | 1228 | if (is_current_pgrp_orphaned()) { |
1223 | return -EIO; | 1229 | ret = -EIO; |
1230 | goto out; | ||
1231 | } | ||
1224 | kill_pgrp(task_pgrp(current), SIGTTOU, 1); | 1232 | kill_pgrp(task_pgrp(current), SIGTTOU, 1); |
1225 | set_thread_flag(TIF_SIGPENDING); | 1233 | set_thread_flag(TIF_SIGPENDING); |
1226 | return -ERESTARTSYS; | 1234 | ret = -ERESTARTSYS; |
1235 | out: | ||
1236 | spin_unlock_irqrestore(&tty->ctrl_lock, flags); | ||
1237 | return ret; | ||
1227 | } | 1238 | } |
1228 | 1239 | ||
1229 | EXPORT_SYMBOL(tty_check_change); | 1240 | EXPORT_SYMBOL(tty_check_change); |
@@ -1403,6 +1414,7 @@ static void do_tty_hangup(struct work_struct *work) | |||
1403 | struct task_struct *p; | 1414 | struct task_struct *p; |
1404 | struct tty_ldisc *ld; | 1415 | struct tty_ldisc *ld; |
1405 | int closecount = 0, n; | 1416 | int closecount = 0, n; |
1417 | unsigned long flags; | ||
1406 | 1418 | ||
1407 | if (!tty) | 1419 | if (!tty) |
1408 | return; | 1420 | return; |
@@ -1479,19 +1491,24 @@ static void do_tty_hangup(struct work_struct *work) | |||
1479 | __group_send_sig_info(SIGHUP, SEND_SIG_PRIV, p); | 1491 | __group_send_sig_info(SIGHUP, SEND_SIG_PRIV, p); |
1480 | __group_send_sig_info(SIGCONT, SEND_SIG_PRIV, p); | 1492 | __group_send_sig_info(SIGCONT, SEND_SIG_PRIV, p); |
1481 | put_pid(p->signal->tty_old_pgrp); /* A noop */ | 1493 | put_pid(p->signal->tty_old_pgrp); /* A noop */ |
1494 | spin_lock_irqsave(&tty->ctrl_lock, flags); | ||
1482 | if (tty->pgrp) | 1495 | if (tty->pgrp) |
1483 | p->signal->tty_old_pgrp = get_pid(tty->pgrp); | 1496 | p->signal->tty_old_pgrp = get_pid(tty->pgrp); |
1497 | spin_unlock_irqrestore(&tty->ctrl_lock, flags); | ||
1484 | spin_unlock_irq(&p->sighand->siglock); | 1498 | spin_unlock_irq(&p->sighand->siglock); |
1485 | } while_each_pid_task(tty->session, PIDTYPE_SID, p); | 1499 | } while_each_pid_task(tty->session, PIDTYPE_SID, p); |
1486 | } | 1500 | } |
1487 | read_unlock(&tasklist_lock); | 1501 | read_unlock(&tasklist_lock); |
1488 | 1502 | ||
1503 | spin_lock_irqsave(&tty->ctrl_lock, flags); | ||
1489 | tty->flags = 0; | 1504 | tty->flags = 0; |
1490 | put_pid(tty->session); | 1505 | put_pid(tty->session); |
1491 | put_pid(tty->pgrp); | 1506 | put_pid(tty->pgrp); |
1492 | tty->session = NULL; | 1507 | tty->session = NULL; |
1493 | tty->pgrp = NULL; | 1508 | tty->pgrp = NULL; |
1494 | tty->ctrl_status = 0; | 1509 | tty->ctrl_status = 0; |
1510 | spin_unlock_irqrestore(&tty->ctrl_lock, flags); | ||
1511 | |||
1495 | /* | 1512 | /* |
1496 | * If one of the devices matches a console pointer, we | 1513 | * If one of the devices matches a console pointer, we |
1497 | * cannot just call hangup() because that will cause | 1514 | * cannot just call hangup() because that will cause |
@@ -1666,10 +1683,13 @@ void disassociate_ctty(int on_exit) | |||
1666 | /* It is possible that do_tty_hangup has free'd this tty */ | 1683 | /* It is possible that do_tty_hangup has free'd this tty */ |
1667 | tty = get_current_tty(); | 1684 | tty = get_current_tty(); |
1668 | if (tty) { | 1685 | if (tty) { |
1686 | unsigned long flags; | ||
1687 | spin_lock_irqsave(&tty->ctrl_lock, flags); | ||
1669 | put_pid(tty->session); | 1688 | put_pid(tty->session); |
1670 | put_pid(tty->pgrp); | 1689 | put_pid(tty->pgrp); |
1671 | tty->session = NULL; | 1690 | tty->session = NULL; |
1672 | tty->pgrp = NULL; | 1691 | tty->pgrp = NULL; |
1692 | spin_unlock_irqrestore(&tty->ctrl_lock, flags); | ||
1673 | } else { | 1693 | } else { |
1674 | #ifdef TTY_DEBUG_HANGUP | 1694 | #ifdef TTY_DEBUG_HANGUP |
1675 | printk(KERN_DEBUG "error attempted to write to tty [0x%p]" | 1695 | printk(KERN_DEBUG "error attempted to write to tty [0x%p]" |
@@ -1785,10 +1805,8 @@ EXPORT_SYMBOL(start_tty); | |||
1785 | * for hung up devices before calling the line discipline method. | 1805 | * for hung up devices before calling the line discipline method. |
1786 | * | 1806 | * |
1787 | * Locking: | 1807 | * Locking: |
1788 | * Locks the line discipline internally while needed | 1808 | * Locks the line discipline internally while needed. Multiple |
1789 | * For historical reasons the line discipline read method is | 1809 | * read calls may be outstanding in parallel. |
1790 | * invoked under the BKL. This will go away in time so do not rely on it | ||
1791 | * in new code. Multiple read calls may be outstanding in parallel. | ||
1792 | */ | 1810 | */ |
1793 | 1811 | ||
1794 | static ssize_t tty_read(struct file *file, char __user *buf, size_t count, | 1812 | static ssize_t tty_read(struct file *file, char __user *buf, size_t count, |
@@ -2888,6 +2906,7 @@ static unsigned int tty_poll(struct file *filp, poll_table *wait) | |||
2888 | static int tty_fasync(int fd, struct file *filp, int on) | 2906 | static int tty_fasync(int fd, struct file *filp, int on) |
2889 | { | 2907 | { |
2890 | struct tty_struct *tty; | 2908 | struct tty_struct *tty; |
2909 | unsigned long flags; | ||
2891 | int retval; | 2910 | int retval; |
2892 | 2911 | ||
2893 | tty = (struct tty_struct *)filp->private_data; | 2912 | tty = (struct tty_struct *)filp->private_data; |
@@ -2903,6 +2922,7 @@ static int tty_fasync(int fd, struct file *filp, int on) | |||
2903 | struct pid *pid; | 2922 | struct pid *pid; |
2904 | if (!waitqueue_active(&tty->read_wait)) | 2923 | if (!waitqueue_active(&tty->read_wait)) |
2905 | tty->minimum_to_wake = 1; | 2924 | tty->minimum_to_wake = 1; |
2925 | spin_lock_irqsave(&tty->ctrl_lock, flags); | ||
2906 | if (tty->pgrp) { | 2926 | if (tty->pgrp) { |
2907 | pid = tty->pgrp; | 2927 | pid = tty->pgrp; |
2908 | type = PIDTYPE_PGID; | 2928 | type = PIDTYPE_PGID; |
@@ -2910,6 +2930,7 @@ static int tty_fasync(int fd, struct file *filp, int on) | |||
2910 | pid = task_pid(current); | 2930 | pid = task_pid(current); |
2911 | type = PIDTYPE_PID; | 2931 | type = PIDTYPE_PID; |
2912 | } | 2932 | } |
2933 | spin_unlock_irqrestore(&tty->ctrl_lock, flags); | ||
2913 | retval = __f_setown(filp, pid, type, 0); | 2934 | retval = __f_setown(filp, pid, type, 0); |
2914 | if (retval) | 2935 | if (retval) |
2915 | return retval; | 2936 | return retval; |
@@ -2995,6 +3016,8 @@ static int tiocswinsz(struct tty_struct *tty, struct tty_struct *real_tty, | |||
2995 | struct winsize __user *arg) | 3016 | struct winsize __user *arg) |
2996 | { | 3017 | { |
2997 | struct winsize tmp_ws; | 3018 | struct winsize tmp_ws; |
3019 | struct pid *pgrp, *rpgrp; | ||
3020 | unsigned long flags; | ||
2998 | 3021 | ||
2999 | if (copy_from_user(&tmp_ws, arg, sizeof(*arg))) | 3022 | if (copy_from_user(&tmp_ws, arg, sizeof(*arg))) |
3000 | return -EFAULT; | 3023 | return -EFAULT; |
@@ -3012,10 +3035,21 @@ static int tiocswinsz(struct tty_struct *tty, struct tty_struct *real_tty, | |||
3012 | } | 3035 | } |
3013 | } | 3036 | } |
3014 | #endif | 3037 | #endif |
3015 | if (tty->pgrp) | 3038 | /* Get the PID values and reference them so we can |
3016 | kill_pgrp(tty->pgrp, SIGWINCH, 1); | 3039 | avoid holding the tty ctrl lock while sending signals */ |
3017 | if ((real_tty->pgrp != tty->pgrp) && real_tty->pgrp) | 3040 | spin_lock_irqsave(&tty->ctrl_lock, flags); |
3018 | kill_pgrp(real_tty->pgrp, SIGWINCH, 1); | 3041 | pgrp = get_pid(tty->pgrp); |
3042 | rpgrp = get_pid(real_tty->pgrp); | ||
3043 | spin_unlock_irqrestore(&tty->ctrl_lock, flags); | ||
3044 | |||
3045 | if (pgrp) | ||
3046 | kill_pgrp(pgrp, SIGWINCH, 1); | ||
3047 | if (rpgrp != pgrp && rpgrp) | ||
3048 | kill_pgrp(rpgrp, SIGWINCH, 1); | ||
3049 | |||
3050 | put_pid(pgrp); | ||
3051 | put_pid(rpgrp); | ||
3052 | |||
3019 | tty->winsize = tmp_ws; | 3053 | tty->winsize = tmp_ws; |
3020 | real_tty->winsize = tmp_ws; | 3054 | real_tty->winsize = tmp_ws; |
3021 | done: | 3055 | done: |
@@ -3171,7 +3205,7 @@ static int tiocgpgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t | |||
3171 | * Set the process group of the tty to the session passed. Only | 3205 | * Set the process group of the tty to the session passed. Only |
3172 | * permitted where the tty session is our session. | 3206 | * permitted where the tty session is our session. |
3173 | * | 3207 | * |
3174 | * Locking: RCU | 3208 | * Locking: RCU, ctrl lock |
3175 | */ | 3209 | */ |
3176 | 3210 | ||
3177 | static int tiocspgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t __user *p) | 3211 | static int tiocspgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t __user *p) |
@@ -3179,6 +3213,7 @@ static int tiocspgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t | |||
3179 | struct pid *pgrp; | 3213 | struct pid *pgrp; |
3180 | pid_t pgrp_nr; | 3214 | pid_t pgrp_nr; |
3181 | int retval = tty_check_change(real_tty); | 3215 | int retval = tty_check_change(real_tty); |
3216 | unsigned long flags; | ||
3182 | 3217 | ||
3183 | if (retval == -EIO) | 3218 | if (retval == -EIO) |
3184 | return -ENOTTY; | 3219 | return -ENOTTY; |
@@ -3201,8 +3236,10 @@ static int tiocspgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t | |||
3201 | if (session_of_pgrp(pgrp) != task_session(current)) | 3236 | if (session_of_pgrp(pgrp) != task_session(current)) |
3202 | goto out_unlock; | 3237 | goto out_unlock; |
3203 | retval = 0; | 3238 | retval = 0; |
3239 | spin_lock_irqsave(&tty->ctrl_lock, flags); | ||
3204 | put_pid(real_tty->pgrp); | 3240 | put_pid(real_tty->pgrp); |
3205 | real_tty->pgrp = get_pid(pgrp); | 3241 | real_tty->pgrp = get_pid(pgrp); |
3242 | spin_unlock_irqrestore(&tty->ctrl_lock, flags); | ||
3206 | out_unlock: | 3243 | out_unlock: |
3207 | rcu_read_unlock(); | 3244 | rcu_read_unlock(); |
3208 | return retval; | 3245 | return retval; |
@@ -4077,14 +4114,19 @@ void proc_clear_tty(struct task_struct *p) | |||
4077 | } | 4114 | } |
4078 | EXPORT_SYMBOL(proc_clear_tty); | 4115 | EXPORT_SYMBOL(proc_clear_tty); |
4079 | 4116 | ||
4117 | /* Called under the sighand lock */ | ||
4118 | |||
4080 | static void __proc_set_tty(struct task_struct *tsk, struct tty_struct *tty) | 4119 | static void __proc_set_tty(struct task_struct *tsk, struct tty_struct *tty) |
4081 | { | 4120 | { |
4082 | if (tty) { | 4121 | if (tty) { |
4083 | /* We should not have a session or pgrp to here but.... */ | 4122 | unsigned long flags; |
4123 | /* We should not have a session or pgrp to put here but.... */ | ||
4124 | spin_lock_irqsave(&tty->ctrl_lock, flags); | ||
4084 | put_pid(tty->session); | 4125 | put_pid(tty->session); |
4085 | put_pid(tty->pgrp); | 4126 | put_pid(tty->pgrp); |
4086 | tty->session = get_pid(task_session(tsk)); | ||
4087 | tty->pgrp = get_pid(task_pgrp(tsk)); | 4127 | tty->pgrp = get_pid(task_pgrp(tsk)); |
4128 | spin_unlock_irqrestore(&tty->ctrl_lock, flags); | ||
4129 | tty->session = get_pid(task_session(tsk)); | ||
4088 | } | 4130 | } |
4089 | put_pid(tsk->signal->tty_old_pgrp); | 4131 | put_pid(tsk->signal->tty_old_pgrp); |
4090 | tsk->signal->tty = tty; | 4132 | tsk->signal->tty = tty; |
diff --git a/drivers/char/vt.c b/drivers/char/vt.c index e64f0bf3624e..c71d1d0f13b9 100644 --- a/drivers/char/vt.c +++ b/drivers/char/vt.c | |||
@@ -909,15 +909,21 @@ int vc_resize(struct vc_data *vc, unsigned int cols, unsigned int lines) | |||
909 | 909 | ||
910 | if (vc->vc_tty) { | 910 | if (vc->vc_tty) { |
911 | struct winsize ws, *cws = &vc->vc_tty->winsize; | 911 | struct winsize ws, *cws = &vc->vc_tty->winsize; |
912 | unsigned long flags; | ||
912 | 913 | ||
913 | memset(&ws, 0, sizeof(ws)); | 914 | memset(&ws, 0, sizeof(ws)); |
914 | ws.ws_row = vc->vc_rows; | 915 | ws.ws_row = vc->vc_rows; |
915 | ws.ws_col = vc->vc_cols; | 916 | ws.ws_col = vc->vc_cols; |
916 | ws.ws_ypixel = vc->vc_scan_lines; | 917 | ws.ws_ypixel = vc->vc_scan_lines; |
918 | |||
919 | mutex_lock(&vc->vc_tty->termios_mutex); | ||
920 | spin_lock_irqsave(&vc->vc_tty->ctrl_lock, flags); | ||
917 | if ((ws.ws_row != cws->ws_row || ws.ws_col != cws->ws_col) && | 921 | if ((ws.ws_row != cws->ws_row || ws.ws_col != cws->ws_col) && |
918 | vc->vc_tty->pgrp) | 922 | vc->vc_tty->pgrp) |
919 | kill_pgrp(vc->vc_tty->pgrp, SIGWINCH, 1); | 923 | kill_pgrp(vc->vc_tty->pgrp, SIGWINCH, 1); |
924 | spin_unlock_irqrestore(&vc->vc_tty->ctrl_lock, flags); | ||
920 | *cws = ws; | 925 | *cws = ws; |
926 | mutex_unlock(&vc->vc_tty->termios_mutex); | ||
921 | } | 927 | } |
922 | 928 | ||
923 | if (CON_IS_VISIBLE(vc)) | 929 | if (CON_IS_VISIBLE(vc)) |
diff --git a/include/linux/tty.h b/include/linux/tty.h index 4d3702bade03..381085e45cca 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h | |||
@@ -184,21 +184,22 @@ struct tty_struct { | |||
184 | struct tty_ldisc ldisc; | 184 | struct tty_ldisc ldisc; |
185 | struct mutex termios_mutex; | 185 | struct mutex termios_mutex; |
186 | spinlock_t ctrl_lock; | 186 | spinlock_t ctrl_lock; |
187 | /* Termios values are protected by the termios mutex */ | ||
187 | struct ktermios *termios, *termios_locked; | 188 | struct ktermios *termios, *termios_locked; |
188 | char name[64]; | 189 | char name[64]; |
189 | struct pid *pgrp; | 190 | struct pid *pgrp; /* Protected by ctrl lock */ |
190 | struct pid *session; | 191 | struct pid *session; |
191 | unsigned long flags; | 192 | unsigned long flags; |
192 | int count; | 193 | int count; |
193 | struct winsize winsize; | 194 | struct winsize winsize; /* termios mutex */ |
194 | unsigned char stopped:1, hw_stopped:1, flow_stopped:1, packet:1; | 195 | unsigned char stopped:1, hw_stopped:1, flow_stopped:1, packet:1; |
195 | unsigned char low_latency:1, warned:1; | 196 | unsigned char low_latency:1, warned:1; |
196 | unsigned char ctrl_status; | 197 | unsigned char ctrl_status; /* ctrl_lock */ |
197 | unsigned int receive_room; /* Bytes free for queue */ | 198 | unsigned int receive_room; /* Bytes free for queue */ |
198 | 199 | ||
199 | struct tty_struct *link; | 200 | struct tty_struct *link; |
200 | struct fasync_struct *fasync; | 201 | struct fasync_struct *fasync; |
201 | struct tty_bufhead buf; | 202 | struct tty_bufhead buf; /* Locked internally */ |
202 | int alt_speed; /* For magic substitution of 38400 bps */ | 203 | int alt_speed; /* For magic substitution of 38400 bps */ |
203 | wait_queue_head_t write_wait; | 204 | wait_queue_head_t write_wait; |
204 | wait_queue_head_t read_wait; | 205 | wait_queue_head_t read_wait; |
@@ -212,6 +213,7 @@ struct tty_struct { | |||
212 | /* | 213 | /* |
213 | * The following is data for the N_TTY line discipline. For | 214 | * The following is data for the N_TTY line discipline. For |
214 | * historical reasons, this is included in the tty structure. | 215 | * historical reasons, this is included in the tty structure. |
216 | * Mostly locked by the BKL. | ||
215 | */ | 217 | */ |
216 | unsigned int column; | 218 | unsigned int column; |
217 | unsigned char lnext:1, erasing:1, raw:1, real_raw:1, icanon:1; | 219 | unsigned char lnext:1, erasing:1, raw:1, real_raw:1, icanon:1; |