diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2010-02-07 13:11:23 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2010-02-07 13:26:01 -0500 |
commit | 80e1e823989ec44d8e35bdfddadbddcffec90424 (patch) | |
tree | ad6fd35a0130bc47b082135724834f7db5548c12 /fs | |
parent | 29275254caedfedce960cfe6df24b90cb04fe431 (diff) |
Fix race in tty_fasync() properly
This reverts commit 703625118069 ("tty: fix race in tty_fasync") and
commit b04da8bfdfbb ("fnctl: f_modown should call write_lock_irqsave/
restore") that tried to fix up some of the fallout but was incomplete.
It turns out that we really cannot hold 'tty->ctrl_lock' over calling
__f_setown, because not only did that cause problems with interrupt
disables (which the second commit fixed), it also causes a potential
ABBA deadlock due to lock ordering.
Thanks to Tetsuo Handa for following up on the issue, and running
lockdep to show the problem. It goes roughly like this:
- f_getown gets filp->f_owner.lock for reading without interrupts
disabled, so an interrupt that happens while that lock is held can
cause a lockdep chain from f_owner.lock -> sighand->siglock.
- at the same time, the tty->ctrl_lock -> f_owner.lock chain that
commit 703625118069 introduced, together with the pre-existing
sighand->siglock -> tty->ctrl_lock chain means that we have a lock
dependency the other way too.
So instead of extending tty->ctrl_lock over the whole __f_setown() call,
we now just take a reference to the 'pid' structure while holding the
lock, and then release it after having done the __f_setown. That still
guarantees that 'struct pid' won't go away from under us, which is all
we really ever needed.
Reported-and-tested-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Acked-by: Greg Kroah-Hartman <gregkh@suse.de>
Acked-by: Américo Wang <xiyou.wangcong@gmail.com>
Cc: stable@kernel.org
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/fcntl.c | 6 |
1 files changed, 2 insertions, 4 deletions
diff --git a/fs/fcntl.c b/fs/fcntl.c index 5ef953e6f908..97e01dc0d95f 100644 --- a/fs/fcntl.c +++ b/fs/fcntl.c | |||
@@ -199,9 +199,7 @@ static int setfl(int fd, struct file * filp, unsigned long arg) | |||
199 | static void f_modown(struct file *filp, struct pid *pid, enum pid_type type, | 199 | static void f_modown(struct file *filp, struct pid *pid, enum pid_type type, |
200 | int force) | 200 | int force) |
201 | { | 201 | { |
202 | unsigned long flags; | 202 | write_lock_irq(&filp->f_owner.lock); |
203 | |||
204 | write_lock_irqsave(&filp->f_owner.lock, flags); | ||
205 | if (force || !filp->f_owner.pid) { | 203 | if (force || !filp->f_owner.pid) { |
206 | put_pid(filp->f_owner.pid); | 204 | put_pid(filp->f_owner.pid); |
207 | filp->f_owner.pid = get_pid(pid); | 205 | filp->f_owner.pid = get_pid(pid); |
@@ -213,7 +211,7 @@ static void f_modown(struct file *filp, struct pid *pid, enum pid_type type, | |||
213 | filp->f_owner.euid = cred->euid; | 211 | filp->f_owner.euid = cred->euid; |
214 | } | 212 | } |
215 | } | 213 | } |
216 | write_unlock_irqrestore(&filp->f_owner.lock, flags); | 214 | write_unlock_irq(&filp->f_owner.lock); |
217 | } | 215 | } |
218 | 216 | ||
219 | int __f_setown(struct file *filp, struct pid *pid, enum pid_type type, | 217 | int __f_setown(struct file *filp, struct pid *pid, enum pid_type type, |