diff options
| author | Al Viro <viro@zeniv.linux.org.uk> | 2015-02-06 02:07:45 -0500 |
|---|---|---|
| committer | Al Viro <viro@zeniv.linux.org.uk> | 2015-02-17 22:23:32 -0500 |
| commit | f01d35a15fa04162a58b95970fc01fa70ec9dacd (patch) | |
| tree | 4a54918fe9e942e5826d6e4ddfcda6bc979af21e | |
| parent | 70e60d917e91fff2237095b8950810effa2b1a50 (diff) | |
gadgetfs: use-after-free in ->aio_read()
AIO_PREAD requests call ->aio_read() with iovec on caller's stack, so if
we are going to access it asynchronously, we'd better get ourselves
a copy - the one on kernel stack of aio_run_iocb() won't be there
anymore. function/f_fs.c take care of doing that, legacy/inode.c
doesn't...
Cc: stable@vger.kernel.org
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
| -rw-r--r-- | drivers/usb/gadget/legacy/inode.c | 15 |
1 files changed, 12 insertions, 3 deletions
diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c index db49ec4c748e..9fbbaa041a31 100644 --- a/drivers/usb/gadget/legacy/inode.c +++ b/drivers/usb/gadget/legacy/inode.c | |||
| @@ -566,7 +566,6 @@ static ssize_t ep_copy_to_user(struct kiocb_priv *priv) | |||
| 566 | if (total == 0) | 566 | if (total == 0) |
| 567 | break; | 567 | break; |
| 568 | } | 568 | } |
| 569 | |||
| 570 | return len; | 569 | return len; |
| 571 | } | 570 | } |
| 572 | 571 | ||
| @@ -585,6 +584,7 @@ static void ep_user_copy_worker(struct work_struct *work) | |||
| 585 | aio_complete(iocb, ret, ret); | 584 | aio_complete(iocb, ret, ret); |
| 586 | 585 | ||
| 587 | kfree(priv->buf); | 586 | kfree(priv->buf); |
| 587 | kfree(priv->iv); | ||
| 588 | kfree(priv); | 588 | kfree(priv); |
| 589 | } | 589 | } |
| 590 | 590 | ||
| @@ -605,6 +605,7 @@ static void ep_aio_complete(struct usb_ep *ep, struct usb_request *req) | |||
| 605 | */ | 605 | */ |
| 606 | if (priv->iv == NULL || unlikely(req->actual == 0)) { | 606 | if (priv->iv == NULL || unlikely(req->actual == 0)) { |
| 607 | kfree(req->buf); | 607 | kfree(req->buf); |
| 608 | kfree(priv->iv); | ||
| 608 | kfree(priv); | 609 | kfree(priv); |
| 609 | iocb->private = NULL; | 610 | iocb->private = NULL; |
| 610 | /* aio_complete() reports bytes-transferred _and_ faults */ | 611 | /* aio_complete() reports bytes-transferred _and_ faults */ |
| @@ -640,7 +641,7 @@ ep_aio_rwtail( | |||
| 640 | struct usb_request *req; | 641 | struct usb_request *req; |
| 641 | ssize_t value; | 642 | ssize_t value; |
| 642 | 643 | ||
| 643 | priv = kmalloc(sizeof *priv, GFP_KERNEL); | 644 | priv = kzalloc(sizeof *priv, GFP_KERNEL); |
| 644 | if (!priv) { | 645 | if (!priv) { |
| 645 | value = -ENOMEM; | 646 | value = -ENOMEM; |
| 646 | fail: | 647 | fail: |
| @@ -649,7 +650,14 @@ fail: | |||
| 649 | } | 650 | } |
| 650 | iocb->private = priv; | 651 | iocb->private = priv; |
| 651 | priv->iocb = iocb; | 652 | priv->iocb = iocb; |
| 652 | priv->iv = iv; | 653 | if (iv) { |
| 654 | priv->iv = kmemdup(iv, nr_segs * sizeof(struct iovec), | ||
| 655 | GFP_KERNEL); | ||
| 656 | if (!priv->iv) { | ||
| 657 | kfree(priv); | ||
| 658 | goto fail; | ||
| 659 | } | ||
| 660 | } | ||
| 653 | priv->nr_segs = nr_segs; | 661 | priv->nr_segs = nr_segs; |
| 654 | INIT_WORK(&priv->work, ep_user_copy_worker); | 662 | INIT_WORK(&priv->work, ep_user_copy_worker); |
| 655 | 663 | ||
| @@ -689,6 +697,7 @@ fail: | |||
| 689 | mutex_unlock(&epdata->lock); | 697 | mutex_unlock(&epdata->lock); |
| 690 | 698 | ||
| 691 | if (unlikely(value)) { | 699 | if (unlikely(value)) { |
| 700 | kfree(priv->iv); | ||
| 692 | kfree(priv); | 701 | kfree(priv); |
| 693 | put_ep(epdata); | 702 | put_ep(epdata); |
| 694 | } else | 703 | } else |
