diff options
author | Daniel De Graaf <dgdegra@tycho.nsa.gov> | 2013-01-02 17:57:11 -0500 |
---|---|---|
committer | Konrad Rzeszutek Wilk <konrad.wilk@oracle.com> | 2013-01-15 16:01:06 -0500 |
commit | 2512f298cb9886e06938e761c9e924c8448d9ab8 (patch) | |
tree | 959938beb9ef789e72217844d159c95604817ac5 | |
parent | 99beae6cb8f4dd5dab81a370b79c3b1085848d89 (diff) |
xen/gntdev: fix unsafe vma access
In gntdev_ioctl_get_offset_for_vaddr, we need to hold mmap_sem while
calling find_vma() to avoid potentially having the result freed out from
under us. Similarly, the MMU notifier functions need to synchronize with
gntdev_vma_close to avoid map->vma being freed during their iteration.
Signed-off-by: Daniel De Graaf <dgdegra@tycho.nsa.gov>
Reported-by: Al Viro <viro@ZenIV.linux.org.uk>
Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
-rw-r--r-- | drivers/xen/gntdev.c | 29 |
1 files changed, 24 insertions, 5 deletions
diff --git a/drivers/xen/gntdev.c b/drivers/xen/gntdev.c index 2e22df2f7a3f..cca62cc8549b 100644 --- a/drivers/xen/gntdev.c +++ b/drivers/xen/gntdev.c | |||
@@ -378,7 +378,20 @@ static void gntdev_vma_close(struct vm_area_struct *vma) | |||
378 | struct grant_map *map = vma->vm_private_data; | 378 | struct grant_map *map = vma->vm_private_data; |
379 | 379 | ||
380 | pr_debug("gntdev_vma_close %p\n", vma); | 380 | pr_debug("gntdev_vma_close %p\n", vma); |
381 | map->vma = NULL; | 381 | if (use_ptemod) { |
382 | struct file *file = vma->vm_file; | ||
383 | struct gntdev_priv *priv = file->private_data; | ||
384 | /* It is possible that an mmu notifier could be running | ||
385 | * concurrently, so take priv->lock to ensure that the vma won't | ||
386 | * vanishing during the unmap_grant_pages call, since we will | ||
387 | * spin here until that completes. Such a concurrent call will | ||
388 | * not do any unmapping, since that has been done prior to | ||
389 | * closing the vma, but it may still iterate the unmap_ops list. | ||
390 | */ | ||
391 | spin_lock(&priv->lock); | ||
392 | map->vma = NULL; | ||
393 | spin_unlock(&priv->lock); | ||
394 | } | ||
382 | vma->vm_private_data = NULL; | 395 | vma->vm_private_data = NULL; |
383 | gntdev_put_map(map); | 396 | gntdev_put_map(map); |
384 | } | 397 | } |
@@ -579,25 +592,31 @@ static long gntdev_ioctl_get_offset_for_vaddr(struct gntdev_priv *priv, | |||
579 | struct ioctl_gntdev_get_offset_for_vaddr op; | 592 | struct ioctl_gntdev_get_offset_for_vaddr op; |
580 | struct vm_area_struct *vma; | 593 | struct vm_area_struct *vma; |
581 | struct grant_map *map; | 594 | struct grant_map *map; |
595 | int rv = -EINVAL; | ||
582 | 596 | ||
583 | if (copy_from_user(&op, u, sizeof(op)) != 0) | 597 | if (copy_from_user(&op, u, sizeof(op)) != 0) |
584 | return -EFAULT; | 598 | return -EFAULT; |
585 | pr_debug("priv %p, offset for vaddr %lx\n", priv, (unsigned long)op.vaddr); | 599 | pr_debug("priv %p, offset for vaddr %lx\n", priv, (unsigned long)op.vaddr); |
586 | 600 | ||
601 | down_read(¤t->mm->mmap_sem); | ||
587 | vma = find_vma(current->mm, op.vaddr); | 602 | vma = find_vma(current->mm, op.vaddr); |
588 | if (!vma || vma->vm_ops != &gntdev_vmops) | 603 | if (!vma || vma->vm_ops != &gntdev_vmops) |
589 | return -EINVAL; | 604 | goto out_unlock; |
590 | 605 | ||
591 | map = vma->vm_private_data; | 606 | map = vma->vm_private_data; |
592 | if (!map) | 607 | if (!map) |
593 | return -EINVAL; | 608 | goto out_unlock; |
594 | 609 | ||
595 | op.offset = map->index << PAGE_SHIFT; | 610 | op.offset = map->index << PAGE_SHIFT; |
596 | op.count = map->count; | 611 | op.count = map->count; |
612 | rv = 0; | ||
597 | 613 | ||
598 | if (copy_to_user(u, &op, sizeof(op)) != 0) | 614 | out_unlock: |
615 | up_read(¤t->mm->mmap_sem); | ||
616 | |||
617 | if (rv == 0 && copy_to_user(u, &op, sizeof(op)) != 0) | ||
599 | return -EFAULT; | 618 | return -EFAULT; |
600 | return 0; | 619 | return rv; |
601 | } | 620 | } |
602 | 621 | ||
603 | static long gntdev_ioctl_notify(struct gntdev_priv *priv, void __user *u) | 622 | static long gntdev_ioctl_notify(struct gntdev_priv *priv, void __user *u) |