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 | |
| 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')
| -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) | |
