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 |