Bluetooth: Fix racy acquire of rfcomm_dev reference
authorPeter Hurley <peter@hurleysoftware.com>
Mon, 10 Feb 2014 01:59:05 +0000 (20:59 -0500)
committerMarcel Holtmann <marcel@holtmann.org>
Fri, 14 Feb 2014 21:39:29 +0000 (13:39 -0800)
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>
net/bluetooth/rfcomm/tty.c

index a535ef148ef6da4ab5478b33cff7f016330a6332..7cf193f0eea71749fee7de6b8a77f14d666474f5 100644 (file)
@@ -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);