vhost_vdpa_pa_map() adds the IOVA page offset to the user-controlled map
size before computing the number of pages to pin. If that addition wraps,
the code can pin and map fewer pages than the requested IOTLB range.

Reject sizes that overflow the page-count calculation. Also make the
memlock check subtraction-based so a large page count cannot wrap the
pinned page total.

Signed-off-by: Yousef Alhouseen <[email protected]>
---
 drivers/vhost/vdpa.c | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/drivers/vhost/vdpa.c b/drivers/vhost/vdpa.c
index ac55275fa..090cb8693 100644
--- a/drivers/vhost/vdpa.c
+++ b/drivers/vhost/vdpa.c
@@ -1102,6 +1102,8 @@ static int vhost_vdpa_pa_map(struct vhost_vdpa *v,
        unsigned int gup_flags = FOLL_LONGTERM;
        unsigned long npages, cur_base, map_pfn, last_pfn = 0;
        unsigned long lock_limit, sz2pin, nchunks, i;
+       unsigned long page_offset;
+       u64 pinned_vm;
        u64 start = iova;
        long pinned;
        int ret = 0;
@@ -1114,7 +1116,12 @@ static int vhost_vdpa_pa_map(struct vhost_vdpa *v,
        if (perm & VHOST_ACCESS_WO)
                gup_flags |= FOLL_WRITE;
 
-       npages = PFN_UP(size + (iova & ~PAGE_MASK));
+       page_offset = iova & ~PAGE_MASK;
+       if (size > ULONG_MAX - page_offset) {
+               ret = -EINVAL;
+               goto free;
+       }
+       npages = PFN_UP(size + page_offset);
        if (!npages) {
                ret = -EINVAL;
                goto free;
@@ -1123,7 +1130,8 @@ static int vhost_vdpa_pa_map(struct vhost_vdpa *v,
        mmap_read_lock(dev->mm);
 
        lock_limit = PFN_DOWN(rlimit(RLIMIT_MEMLOCK));
-       if (npages + atomic64_read(&dev->mm->pinned_vm) > lock_limit) {
+       pinned_vm = atomic64_read(&dev->mm->pinned_vm);
+       if (npages > lock_limit || pinned_vm > lock_limit - npages) {
                ret = -ENOMEM;
                goto unlock;
        }
-- 
2.54.0


Reply via email to