[PATCH 3/2] tty-ldisc: be more careful in 'put_ldisc' locking

From: Linus Torvalds
Date: Mon Aug 03 2009 - 18:58:50 EST




From: Linus Torvalds <torvalds@xxxxxxxxxxxxxxxxxxxx>
Date: Mon, 3 Aug 2009 14:55:24 -0700
Subject: [PATCH 3/2] tty-ldisc: be more careful in 'put_ldisc' locking

Use 'atomic_dec_and_lock()' to make sure that we always hold the
tty_ldisc_lock when the ldisc count goes to zero. That way we can never
race against 'tty_ldisc_try()' increasing the count again.

Reported-by: OGAWA Hirofumi <hirofumi@xxxxxxxxxxxxxxxxxx>
Signed-off-by: Linus Torvalds <torvalds@xxxxxxxxxxxxxxxxxxxx>
---
drivers/char/tty_ldisc.c | 17 ++++++++++++-----
1 files changed, 12 insertions(+), 5 deletions(-)

Ok, this is strictly speaking a bugfix, but the race is so unlikely that I
doubt that you'll see it in testing.

So testing just patches 1-2 is fine, but this is a good idea on top of
them.

I'll be sending out two further patches after this that are pure cleanups
(numbered 4/2 and 5/2). Again, testing them would be wonderful, but not
essential - they're not as interesting or important as 1-2 were.

On Mon, 3 Aug 2009, Linus Torvalds wrote:
>
> Ogawa found a race in my original 2/2, and Greg has a small fix pending,
> but that almost certainly won't realistically matter for any real-life
> testing, so you don't really need to worry about it.
>
> But I'll forward that patch (and another couple cleanup patch) for you for
> testing after I've verified it myself. But don't feel like you have to
> worry about those extra patches - testing the initial refcount handling is
> the thing that matters most, the thing I have pending really is just
> details.
>
> Linus
>

diff --git a/drivers/char/tty_ldisc.c b/drivers/char/tty_ldisc.c
index be55dfc..1733d34 100644
--- a/drivers/char/tty_ldisc.c
+++ b/drivers/char/tty_ldisc.c
@@ -55,25 +55,32 @@ static inline struct tty_ldisc *get_ldisc(struct tty_ldisc *ld)
return ld;
}

-static inline void put_ldisc(struct tty_ldisc *ld)
+static void put_ldisc(struct tty_ldisc *ld)
{
+ unsigned long flags;
+
if (WARN_ON_ONCE(!ld))
return;

/*
* If this is the last user, free the ldisc, and
* release the ldisc ops.
+ *
+ * We really want an "atomic_dec_and_lock_irqsave()",
+ * but we don't have it, so this does it by hand.
*/
- if (atomic_dec_and_test(&ld->users)) {
- unsigned long flags;
+ local_irq_save(flags);
+ if (atomic_dec_and_lock(&ld->users, &tty_ldisc_lock)) {
struct tty_ldisc_ops *ldo = ld->ops;

- kfree(ld);
- spin_lock_irqsave(&tty_ldisc_lock, flags);
ldo->refcount--;
module_put(ldo->owner);
spin_unlock_irqrestore(&tty_ldisc_lock, flags);
+
+ kfree(ld);
+ return;
}
+ local_irq_restore(flags);
}

/**
--
1.6.4.21.g73b866

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/