diff options
author | Peter Hurley <peter@hurleysoftware.com> | 2014-02-09 20:59:05 -0500 |
---|---|---|
committer | Marcel Holtmann <marcel@holtmann.org> | 2014-02-14 13:39:29 -0800 |
commit | 082a1532fc7607727f759c069eb8dd9fa5ae3f37 (patch) | |
tree | 15f3c98d06aac088502669dd8e35291e399bd734 /net/bluetooth/rfcomm/tty.c | |
parent | c0fdfb80382e4901473ce0e31d1e7833c1d297be (diff) |
Bluetooth: Fix racy acquire of rfcomm_dev reference
rfcomm_dev_get() can return a rfcomm_dev reference for a
device for which destruction may be commencing. This can happen
on tty destruction, which calls rfcomm_tty_cleanup(), the last
port reference may have been released but RFCOMM_TTY_RELEASED
was not set. The following race is also possible:
CPU 0 | CPU 1
| rfcomm_release_dev
rfcomm_dev_get | .
spin_lock | .
dev = __rfcomm_dev_get | .
if dev | .
if test_bit(TTY_RELEASED) | .
| !test_and_set_bit(TTY_RELEASED)
| tty_port_put <<<< last reference
else |
tty_port_get |
The reference acquire is bogus because destruction will commence
with the release of the last reference.
Ignore the external state change of TTY_RELEASED and instead rely
on the reference acquire itself to determine if the reference is
valid.
Cc: Jiri Slaby <jslaby@suse.cz>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Peter Hurley <peter@hurleysoftware.com>
Tested-By: Alexander Holler <holler@ahsoftware.de>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
Diffstat (limited to 'net/bluetooth/rfcomm/tty.c')
-rw-r--r-- | net/bluetooth/rfcomm/tty.c | 8 |
1 files changed, 2 insertions, 6 deletions
diff --git a/net/bluetooth/rfcomm/tty.c b/net/bluetooth/rfcomm/tty.c index a535ef148ef6..7cf193f0eea7 100644 --- a/net/bluetooth/rfcomm/tty.c +++ b/net/bluetooth/rfcomm/tty.c @@ -157,12 +157,8 @@ static struct rfcomm_dev *rfcomm_dev_get(int id) dev = __rfcomm_dev_get(id); - if (dev) { - if (test_bit(RFCOMM_TTY_RELEASED, &dev->flags)) - dev = NULL; - else - tty_port_get(&dev->port); - } + if (dev && !tty_port_get(&dev->port)) + dev = NULL; spin_unlock(&rfcomm_dev_lock); |