diff options
author | Peter Hurley <peter@hurleysoftware.com> | 2014-02-09 20:59:07 -0500 |
---|---|---|
committer | Marcel Holtmann <marcel@holtmann.org> | 2014-02-14 16:39:29 -0500 |
commit | 1c64834e0624c61735308138e67cc3b527f41621 (patch) | |
tree | 912156866fbaa316a2197f261646737f95a88e57 | |
parent | 960603a54aa0d5f4f1c4f1037bcaee571d03cb1e (diff) |
Bluetooth: Release rfcomm_dev only once
No logic prevents an rfcomm_dev from being released multiple
times. For example, if the rfcomm_dev ref count is large due
to pending tx, then multiple RFCOMMRELEASEDEV ioctls may
mistakenly release the rfcomm_dev too many times. Note that
concurrent ioctls are not required to create this condition.
Introduce RFCOMM_DEV_RELEASED status bit which guarantees the
rfcomm_dev can only be released once.
NB: Since the flags are exported to userspace, introduce the status
field to track state for which userspace should not be aware.
Signed-off-by: Peter Hurley <peter@hurleysoftware.com>
Tested-By: Alexander Holler <holler@ahsoftware.de>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
-rw-r--r-- | include/net/bluetooth/rfcomm.h | 6 | ||||
-rw-r--r-- | net/bluetooth/rfcomm/tty.c | 11 |
2 files changed, 14 insertions, 3 deletions
diff --git a/include/net/bluetooth/rfcomm.h b/include/net/bluetooth/rfcomm.h index c312cfc4e922..b9759eb17cdd 100644 --- a/include/net/bluetooth/rfcomm.h +++ b/include/net/bluetooth/rfcomm.h | |||
@@ -324,11 +324,15 @@ int rfcomm_connect_ind(struct rfcomm_session *s, u8 channel, | |||
324 | #define RFCOMMGETDEVINFO _IOR('R', 211, int) | 324 | #define RFCOMMGETDEVINFO _IOR('R', 211, int) |
325 | #define RFCOMMSTEALDLC _IOW('R', 220, int) | 325 | #define RFCOMMSTEALDLC _IOW('R', 220, int) |
326 | 326 | ||
327 | /* rfcomm_dev.flags bit definitions */ | ||
327 | #define RFCOMM_REUSE_DLC 0 | 328 | #define RFCOMM_REUSE_DLC 0 |
328 | #define RFCOMM_RELEASE_ONHUP 1 | 329 | #define RFCOMM_RELEASE_ONHUP 1 |
329 | #define RFCOMM_HANGUP_NOW 2 | 330 | #define RFCOMM_HANGUP_NOW 2 |
330 | #define RFCOMM_TTY_ATTACHED 3 | 331 | #define RFCOMM_TTY_ATTACHED 3 |
331 | #define RFCOMM_TTY_RELEASED 4 | 332 | #define RFCOMM_DEFUNCT_BIT4 4 /* don't reuse this bit - userspace visible */ |
333 | |||
334 | /* rfcomm_dev.status bit definitions */ | ||
335 | #define RFCOMM_DEV_RELEASED 0 | ||
332 | 336 | ||
333 | struct rfcomm_dev_req { | 337 | struct rfcomm_dev_req { |
334 | s16 dev_id; | 338 | s16 dev_id; |
diff --git a/net/bluetooth/rfcomm/tty.c b/net/bluetooth/rfcomm/tty.c index b385d9985656..d9d4bc89e638 100644 --- a/net/bluetooth/rfcomm/tty.c +++ b/net/bluetooth/rfcomm/tty.c | |||
@@ -51,6 +51,8 @@ struct rfcomm_dev { | |||
51 | unsigned long flags; | 51 | unsigned long flags; |
52 | int err; | 52 | int err; |
53 | 53 | ||
54 | unsigned long status; /* don't export to userspace */ | ||
55 | |||
54 | bdaddr_t src; | 56 | bdaddr_t src; |
55 | bdaddr_t dst; | 57 | bdaddr_t dst; |
56 | u8 channel; | 58 | u8 channel; |
@@ -423,6 +425,12 @@ static int rfcomm_release_dev(void __user *arg) | |||
423 | return -EPERM; | 425 | return -EPERM; |
424 | } | 426 | } |
425 | 427 | ||
428 | /* only release once */ | ||
429 | if (test_and_set_bit(RFCOMM_DEV_RELEASED, &dev->status)) { | ||
430 | tty_port_put(&dev->port); | ||
431 | return -EALREADY; | ||
432 | } | ||
433 | |||
426 | if (req.flags & (1 << RFCOMM_HANGUP_NOW)) | 434 | if (req.flags & (1 << RFCOMM_HANGUP_NOW)) |
427 | rfcomm_dlc_close(dev->dlc, 0); | 435 | rfcomm_dlc_close(dev->dlc, 0); |
428 | 436 | ||
@@ -433,8 +441,7 @@ static int rfcomm_release_dev(void __user *arg) | |||
433 | tty_kref_put(tty); | 441 | tty_kref_put(tty); |
434 | } | 442 | } |
435 | 443 | ||
436 | if (!test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags) && | 444 | if (!test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags)) |
437 | !test_and_set_bit(RFCOMM_TTY_RELEASED, &dev->flags)) | ||
438 | tty_port_put(&dev->port); | 445 | tty_port_put(&dev->port); |
439 | 446 | ||
440 | tty_port_put(&dev->port); | 447 | tty_port_put(&dev->port); |