git.openpandora.org
/
pandora-kernel.git
/ blobdiff
commit
grep
author
committer
pickaxe
?
search:
re
summary
|
shortlog
|
log
|
commit
|
commitdiff
|
tree
raw
|
inline
| side by side
[IRDA]: Fix rfcomm use-after-free
[pandora-kernel.git]
/
net
/
bluetooth
/
rfcomm
/
tty.c
diff --git
a/net/bluetooth/rfcomm/tty.c
b/net/bluetooth/rfcomm/tty.c
index
8cd82dc
..
22a8320
100644
(file)
--- a/
net/bluetooth/rfcomm/tty.c
+++ b/
net/bluetooth/rfcomm/tty.c
@@
-74,6
+74,8
@@
struct rfcomm_dev {
wait_queue_head_t wait;
struct tasklet_struct wakeup_task;
wait_queue_head_t wait;
struct tasklet_struct wakeup_task;
+ struct device *tty_dev;
+
atomic_t wmem_alloc;
};
atomic_t wmem_alloc;
};
@@
-93,6
+95,10
@@
static void rfcomm_dev_destruct(struct rfcomm_dev *dev)
BT_DBG("dev %p dlc %p", dev, dlc);
BT_DBG("dev %p dlc %p", dev, dlc);
+ write_lock_bh(&rfcomm_dev_lock);
+ list_del_init(&dev->list);
+ write_unlock_bh(&rfcomm_dev_lock);
+
rfcomm_dlc_lock(dlc);
/* Detach DLC if it's owned by this dev */
if (dlc->owner == dev)
rfcomm_dlc_lock(dlc);
/* Detach DLC if it's owned by this dev */
if (dlc->owner == dev)
@@
-154,8
+160,13
@@
static inline struct rfcomm_dev *rfcomm_dev_get(int id)
read_lock(&rfcomm_dev_lock);
dev = __rfcomm_dev_get(id);
read_lock(&rfcomm_dev_lock);
dev = __rfcomm_dev_get(id);
- if (dev)
- rfcomm_dev_hold(dev);
+
+ if (dev) {
+ if (test_bit(RFCOMM_TTY_RELEASED, &dev->flags))
+ dev = NULL;
+ else
+ rfcomm_dev_hold(dev);
+ }
read_unlock(&rfcomm_dev_lock);
read_unlock(&rfcomm_dev_lock);
@@
-256,12
+267,19
@@
static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc)
out:
write_unlock_bh(&rfcomm_dev_lock);
out:
write_unlock_bh(&rfcomm_dev_lock);
- if (err) {
+ if (err
< 0
) {
kfree(dev);
return err;
}
kfree(dev);
return err;
}
- tty_register_device(rfcomm_tty_driver, dev->id, rfcomm_get_device(dev));
+ dev->tty_dev = tty_register_device(rfcomm_tty_driver, dev->id, NULL);
+
+ if (IS_ERR(dev->tty_dev)) {
+ err = PTR_ERR(dev->tty_dev);
+ list_del(&dev->list);
+ kfree(dev);
+ return err;
+ }
return dev->id;
}
return dev->id;
}
@@
-270,10
+288,7
@@
static void rfcomm_dev_del(struct rfcomm_dev *dev)
{
BT_DBG("dev %p", dev);
{
BT_DBG("dev %p", dev);
- write_lock_bh(&rfcomm_dev_lock);
- list_del_init(&dev->list);
- write_unlock_bh(&rfcomm_dev_lock);
-
+ set_bit(RFCOMM_TTY_RELEASED, &dev->flags);
rfcomm_dev_put(dev);
}
rfcomm_dev_put(dev);
}
@@
-327,7
+342,7
@@
static int rfcomm_create_dev(struct sock *sk, void __user *arg)
if (copy_from_user(&req, arg, sizeof(req)))
return -EFAULT;
if (copy_from_user(&req, arg, sizeof(req)))
return -EFAULT;
- BT_DBG("sk %p dev_id %
i
d flags 0x%x", sk, req.dev_id, req.flags);
+ BT_DBG("sk %p dev_id %d flags 0x%x", sk, req.dev_id, req.flags);
if (req.flags != NOCAP_FLAGS && !capable(CAP_NET_ADMIN))
return -EPERM;
if (req.flags != NOCAP_FLAGS && !capable(CAP_NET_ADMIN))
return -EPERM;
@@
-368,7
+383,7
@@
static int rfcomm_release_dev(void __user *arg)
if (copy_from_user(&req, arg, sizeof(req)))
return -EFAULT;
if (copy_from_user(&req, arg, sizeof(req)))
return -EFAULT;
- BT_DBG("dev_id %
i
d flags 0x%x", req.dev_id, req.flags);
+ BT_DBG("dev_id %d flags 0x%x", req.dev_id, req.flags);
if (!(dev = rfcomm_dev_get(req.dev_id)))
return -ENODEV;
if (!(dev = rfcomm_dev_get(req.dev_id)))
return -ENODEV;
@@
-381,6
+396,10
@@
static int rfcomm_release_dev(void __user *arg)
if (req.flags & (1 << RFCOMM_HANGUP_NOW))
rfcomm_dlc_close(dev->dlc, 0);
if (req.flags & (1 << RFCOMM_HANGUP_NOW))
rfcomm_dlc_close(dev->dlc, 0);
+ /* Shut down TTY synchronously before freeing rfcomm_dev */
+ if (dev->tty)
+ tty_vhangup(dev->tty);
+
rfcomm_dev_del(dev);
rfcomm_dev_put(dev);
return 0;
rfcomm_dev_del(dev);
rfcomm_dev_put(dev);
return 0;
@@
-413,6
+432,8
@@
static int rfcomm_get_dev_list(void __user *arg)
list_for_each(p, &rfcomm_dev_list) {
struct rfcomm_dev *dev = list_entry(p, struct rfcomm_dev, list);
list_for_each(p, &rfcomm_dev_list) {
struct rfcomm_dev *dev = list_entry(p, struct rfcomm_dev, list);
+ if (test_bit(RFCOMM_TTY_RELEASED, &dev->flags))
+ continue;
(di + n)->id = dev->id;
(di + n)->flags = dev->flags;
(di + n)->state = dev->dlc->state;
(di + n)->id = dev->id;
(di + n)->flags = dev->flags;
(di + n)->state = dev->dlc->state;
@@
-515,9
+536,10
@@
static void rfcomm_dev_state_change(struct rfcomm_dlc *dlc, int err)
if (dlc->state == BT_CLOSED) {
if (!dev->tty) {
if (test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags)) {
if (dlc->state == BT_CLOSED) {
if (!dev->tty) {
if (test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags)) {
- rfcomm_dev_hold(dev);
-
rfcomm_dev_del(dev)
;
+ if (rfcomm_dev_get(dev->id) == NULL)
+
return
;
+ rfcomm_dev_del(dev);
/* We have to drop DLC lock here, otherwise
rfcomm_dev_put() will dead lock if it's
the last reference. */
/* We have to drop DLC lock here, otherwise
rfcomm_dev_put() will dead lock if it's
the last reference. */
@@
-630,6
+652,9
@@
static int rfcomm_tty_open(struct tty_struct *tty, struct file *filp)
set_current_state(TASK_RUNNING);
remove_wait_queue(&dev->wait, &wait);
set_current_state(TASK_RUNNING);
remove_wait_queue(&dev->wait, &wait);
+ if (err == 0)
+ device_move(dev->tty_dev, rfcomm_get_device(dev));
+
return err;
}
return err;
}
@@
-642,6
+667,8
@@
static void rfcomm_tty_close(struct tty_struct *tty, struct file *filp)
BT_DBG("tty %p dev %p dlc %p opened %d", tty, dev, dev->dlc, dev->opened);
if (--dev->opened == 0) {
BT_DBG("tty %p dev %p dlc %p opened %d", tty, dev, dev->dlc, dev->opened);
if (--dev->opened == 0) {
+ device_move(dev->tty_dev, NULL);
+
/* Close DLC and dettach TTY */
rfcomm_dlc_close(dev->dlc, 0);
/* Close DLC and dettach TTY */
rfcomm_dlc_close(dev->dlc, 0);
@@
-967,8
+994,12
@@
static void rfcomm_tty_hangup(struct tty_struct *tty)
rfcomm_tty_flush_buffer(tty);
rfcomm_tty_flush_buffer(tty);
- if (test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags))
+ if (test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags)) {
+ if (rfcomm_dev_get(dev->id) == NULL)
+ return;
rfcomm_dev_del(dev);
rfcomm_dev_del(dev);
+ rfcomm_dev_put(dev);
+ }
}
static int rfcomm_tty_read_proc(char *buf, char **start, off_t offset, int len, int *eof, void *unused)
}
static int rfcomm_tty_read_proc(char *buf, char **start, off_t offset, int len, int *eof, void *unused)