aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarcel Holtmann <marcel@holtmann.org>2008-11-30 06:17:29 -0500
committerMarcel Holtmann <marcel@holtmann.org>2008-11-30 06:17:29 -0500
commit9a5df92374d65e2886b92e98dd7d873c533a83ff (patch)
tree0e45cc166ce12c42cb7a94cac2dd82e255552120
parent2e792995e4cb425422dc379c3618447c462756a8 (diff)
Bluetooth: Fix RFCOMM release oops when device is still in use
It turns out that the following sequence of actions will reproduce the oops: 1. Create a new RFCOMM device (using RFCOMMCREATEDEV ioctl) 2. (Try to) open the device 3. Release the RFCOMM device (using RFCOMMRELEASEDEV ioctl) At this point, the "/dev/rfcomm*" device is still in use, but it is gone from the internal list, so the device id can be reused. 4. Create a new RFCOMM device with the same device id as before And now kobject will complain that the TTY already exists. (See http://lkml.org/lkml/2008/7/13/89 for a reproducible test-case.) This patch attempts to correct this by only removing the device from the internal list of devices at the final unregister stage, so that the id won't get reused until the device has been completely destructed. This should be safe as the RFCOMM_TTY_RELEASED bit will be set for the device and prevent the device from being reopened after it has been released. Based on a report from Vegard Nossum <vegard.nossum@gmail.com> Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
-rw-r--r--net/bluetooth/rfcomm/tty.c30
1 files changed, 21 insertions, 9 deletions
diff --git a/net/bluetooth/rfcomm/tty.c b/net/bluetooth/rfcomm/tty.c
index 1e4100bb0b65..111c6c858247 100644
--- a/net/bluetooth/rfcomm/tty.c
+++ b/net/bluetooth/rfcomm/tty.c
@@ -53,7 +53,7 @@ struct rfcomm_dev {
53 char name[12]; 53 char name[12];
54 int id; 54 int id;
55 unsigned long flags; 55 unsigned long flags;
56 int opened; 56 atomic_t opened;
57 int err; 57 int err;
58 58
59 bdaddr_t src; 59 bdaddr_t src;
@@ -256,6 +256,8 @@ static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc)
256 dev->flags = req->flags & 256 dev->flags = req->flags &
257 ((1 << RFCOMM_RELEASE_ONHUP) | (1 << RFCOMM_REUSE_DLC)); 257 ((1 << RFCOMM_RELEASE_ONHUP) | (1 << RFCOMM_REUSE_DLC));
258 258
259 atomic_set(&dev->opened, 0);
260
259 init_waitqueue_head(&dev->wait); 261 init_waitqueue_head(&dev->wait);
260 tasklet_init(&dev->wakeup_task, rfcomm_tty_wakeup, (unsigned long) dev); 262 tasklet_init(&dev->wakeup_task, rfcomm_tty_wakeup, (unsigned long) dev);
261 263
@@ -325,10 +327,10 @@ static void rfcomm_dev_del(struct rfcomm_dev *dev)
325{ 327{
326 BT_DBG("dev %p", dev); 328 BT_DBG("dev %p", dev);
327 329
328 if (test_bit(RFCOMM_TTY_RELEASED, &dev->flags)) 330 BUG_ON(test_and_set_bit(RFCOMM_TTY_RELEASED, &dev->flags));
329 BUG_ON(1); 331
330 else 332 if (atomic_read(&dev->opened) > 0)
331 set_bit(RFCOMM_TTY_RELEASED, &dev->flags); 333 return;
332 334
333 write_lock_bh(&rfcomm_dev_lock); 335 write_lock_bh(&rfcomm_dev_lock);
334 list_del_init(&dev->list); 336 list_del_init(&dev->list);
@@ -684,9 +686,10 @@ static int rfcomm_tty_open(struct tty_struct *tty, struct file *filp)
684 if (!dev) 686 if (!dev)
685 return -ENODEV; 687 return -ENODEV;
686 688
687 BT_DBG("dev %p dst %s channel %d opened %d", dev, batostr(&dev->dst), dev->channel, dev->opened); 689 BT_DBG("dev %p dst %s channel %d opened %d", dev, batostr(&dev->dst),
690 dev->channel, atomic_read(&dev->opened));
688 691
689 if (dev->opened++ != 0) 692 if (atomic_inc_return(&dev->opened) > 1)
690 return 0; 693 return 0;
691 694
692 dlc = dev->dlc; 695 dlc = dev->dlc;
@@ -742,9 +745,10 @@ static void rfcomm_tty_close(struct tty_struct *tty, struct file *filp)
742 if (!dev) 745 if (!dev)
743 return; 746 return;
744 747
745 BT_DBG("tty %p dev %p dlc %p opened %d", tty, dev, dev->dlc, dev->opened); 748 BT_DBG("tty %p dev %p dlc %p opened %d", tty, dev, dev->dlc,
749 atomic_read(&dev->opened));
746 750
747 if (--dev->opened == 0) { 751 if (atomic_dec_and_test(&dev->opened)) {
748 if (dev->tty_dev->parent) 752 if (dev->tty_dev->parent)
749 device_move(dev->tty_dev, NULL); 753 device_move(dev->tty_dev, NULL);
750 754
@@ -758,6 +762,14 @@ static void rfcomm_tty_close(struct tty_struct *tty, struct file *filp)
758 tty->driver_data = NULL; 762 tty->driver_data = NULL;
759 dev->tty = NULL; 763 dev->tty = NULL;
760 rfcomm_dlc_unlock(dev->dlc); 764 rfcomm_dlc_unlock(dev->dlc);
765
766 if (test_bit(RFCOMM_TTY_RELEASED, &dev->flags)) {
767 write_lock_bh(&rfcomm_dev_lock);
768 list_del_init(&dev->list);
769 write_unlock_bh(&rfcomm_dev_lock);
770
771 rfcomm_dev_put(dev);
772 }
761 } 773 }
762 774
763 rfcomm_dev_put(dev); 775 rfcomm_dev_put(dev);