diff options
author | Gianluca Anzolin <gianluca@sottospazio.it> | 2013-07-29 11:08:08 -0400 |
---|---|---|
committer | Gustavo Padovan <gustavo.padovan@collabora.co.uk> | 2013-08-21 10:47:05 -0400 |
commit | 396dc223dd36edd218650d042a07c5e61f022c5b (patch) | |
tree | aba106ba73936f4c271f9f3b2d218a435f2cce5c /net/bluetooth/rfcomm | |
parent | c7882cbd1151011ca8e6fb13530cd09eae1c39ee (diff) |
Bluetooth: Take proper tty_struct references
In net/bluetooth/rfcomm/tty.c the struct tty_struct is used without
taking references. This may lead to a use-after-free of the rfcomm tty.
Fix this by taking references properly, using the tty_port_* helpers
when possible.
The raw assignments of dev->port.tty in rfcomm_tty_open/close are
addressed in the later commit 'rfcomm: Implement .activate, .shutdown
and .carrier_raised methods'.
Signed-off-by: Gianluca Anzolin <gianluca@sottospazio.it>
Reviewed-by: Peter Hurley <peter@hurleysoftware.com>
Signed-off-by: Gustavo Padovan <gustavo.padovan@collabora.co.uk>
Diffstat (limited to 'net/bluetooth/rfcomm')
-rw-r--r-- | net/bluetooth/rfcomm/tty.c | 29 |
1 files changed, 17 insertions, 12 deletions
diff --git a/net/bluetooth/rfcomm/tty.c b/net/bluetooth/rfcomm/tty.c index b6e44ad6cca6..cd7ff370be38 100644 --- a/net/bluetooth/rfcomm/tty.c +++ b/net/bluetooth/rfcomm/tty.c | |||
@@ -333,10 +333,9 @@ static inline unsigned int rfcomm_room(struct rfcomm_dlc *dlc) | |||
333 | static void rfcomm_wfree(struct sk_buff *skb) | 333 | static void rfcomm_wfree(struct sk_buff *skb) |
334 | { | 334 | { |
335 | struct rfcomm_dev *dev = (void *) skb->sk; | 335 | struct rfcomm_dev *dev = (void *) skb->sk; |
336 | struct tty_struct *tty = dev->port.tty; | ||
337 | atomic_sub(skb->truesize, &dev->wmem_alloc); | 336 | atomic_sub(skb->truesize, &dev->wmem_alloc); |
338 | if (test_bit(RFCOMM_TTY_ATTACHED, &dev->flags) && tty) | 337 | if (test_bit(RFCOMM_TTY_ATTACHED, &dev->flags)) |
339 | tty_wakeup(tty); | 338 | tty_port_tty_wakeup(&dev->port); |
340 | tty_port_put(&dev->port); | 339 | tty_port_put(&dev->port); |
341 | } | 340 | } |
342 | 341 | ||
@@ -410,6 +409,7 @@ static int rfcomm_release_dev(void __user *arg) | |||
410 | { | 409 | { |
411 | struct rfcomm_dev_req req; | 410 | struct rfcomm_dev_req req; |
412 | struct rfcomm_dev *dev; | 411 | struct rfcomm_dev *dev; |
412 | struct tty_struct *tty; | ||
413 | 413 | ||
414 | if (copy_from_user(&req, arg, sizeof(req))) | 414 | if (copy_from_user(&req, arg, sizeof(req))) |
415 | return -EFAULT; | 415 | return -EFAULT; |
@@ -429,8 +429,11 @@ static int rfcomm_release_dev(void __user *arg) | |||
429 | rfcomm_dlc_close(dev->dlc, 0); | 429 | rfcomm_dlc_close(dev->dlc, 0); |
430 | 430 | ||
431 | /* Shut down TTY synchronously before freeing rfcomm_dev */ | 431 | /* Shut down TTY synchronously before freeing rfcomm_dev */ |
432 | if (dev->port.tty) | 432 | tty = tty_port_tty_get(&dev->port); |
433 | tty_vhangup(dev->port.tty); | 433 | if (tty) { |
434 | tty_vhangup(tty); | ||
435 | tty_kref_put(tty); | ||
436 | } | ||
434 | 437 | ||
435 | if (!test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags)) | 438 | if (!test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags)) |
436 | rfcomm_dev_del(dev); | 439 | rfcomm_dev_del(dev); |
@@ -563,6 +566,7 @@ static void rfcomm_dev_data_ready(struct rfcomm_dlc *dlc, struct sk_buff *skb) | |||
563 | static void rfcomm_dev_state_change(struct rfcomm_dlc *dlc, int err) | 566 | static void rfcomm_dev_state_change(struct rfcomm_dlc *dlc, int err) |
564 | { | 567 | { |
565 | struct rfcomm_dev *dev = dlc->owner; | 568 | struct rfcomm_dev *dev = dlc->owner; |
569 | struct tty_struct *tty; | ||
566 | if (!dev) | 570 | if (!dev) |
567 | return; | 571 | return; |
568 | 572 | ||
@@ -572,7 +576,8 @@ static void rfcomm_dev_state_change(struct rfcomm_dlc *dlc, int err) | |||
572 | wake_up_interruptible(&dev->wait); | 576 | wake_up_interruptible(&dev->wait); |
573 | 577 | ||
574 | if (dlc->state == BT_CLOSED) { | 578 | if (dlc->state == BT_CLOSED) { |
575 | if (!dev->port.tty) { | 579 | tty = tty_port_tty_get(&dev->port); |
580 | if (!tty) { | ||
576 | if (test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags)) { | 581 | if (test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags)) { |
577 | /* Drop DLC lock here to avoid deadlock | 582 | /* Drop DLC lock here to avoid deadlock |
578 | * 1. rfcomm_dev_get will take rfcomm_dev_lock | 583 | * 1. rfcomm_dev_get will take rfcomm_dev_lock |
@@ -591,8 +596,10 @@ static void rfcomm_dev_state_change(struct rfcomm_dlc *dlc, int err) | |||
591 | tty_port_put(&dev->port); | 596 | tty_port_put(&dev->port); |
592 | rfcomm_dlc_lock(dlc); | 597 | rfcomm_dlc_lock(dlc); |
593 | } | 598 | } |
594 | } else | 599 | } else { |
595 | tty_hangup(dev->port.tty); | 600 | tty_hangup(tty); |
601 | tty_kref_put(tty); | ||
602 | } | ||
596 | } | 603 | } |
597 | } | 604 | } |
598 | 605 | ||
@@ -604,10 +611,8 @@ static void rfcomm_dev_modem_status(struct rfcomm_dlc *dlc, u8 v24_sig) | |||
604 | 611 | ||
605 | BT_DBG("dlc %p dev %p v24_sig 0x%02x", dlc, dev, v24_sig); | 612 | BT_DBG("dlc %p dev %p v24_sig 0x%02x", dlc, dev, v24_sig); |
606 | 613 | ||
607 | if ((dev->modem_status & TIOCM_CD) && !(v24_sig & RFCOMM_V24_DV)) { | 614 | if ((dev->modem_status & TIOCM_CD) && !(v24_sig & RFCOMM_V24_DV)) |
608 | if (dev->port.tty && !C_CLOCAL(dev->port.tty)) | 615 | tty_port_tty_hangup(&dev->port, true); |
609 | tty_hangup(dev->port.tty); | ||
610 | } | ||
611 | 616 | ||
612 | dev->modem_status = | 617 | dev->modem_status = |
613 | ((v24_sig & RFCOMM_V24_RTC) ? (TIOCM_DSR | TIOCM_DTR) : 0) | | 618 | ((v24_sig & RFCOMM_V24_RTC) ? (TIOCM_DSR | TIOCM_DTR) : 0) | |