Ralph Campbell
2019-Aug-23 22:17 UTC
[Nouveau] [PATCH 0/2] mm/hmm: two bug fixes for hmm_range_fault()
I have been working on converting Jerome's hmm_dummy driver and self tests into a stand-alone set of tests to be included in tools/testing/selftests/vm and came across these two bug fixes in the process. The tests aren't quite ready to be posted as a patch. I'm posting the fixes now since I thought they shouldn't wait. They should probably have a fixes line but with all the HMM changes, I wasn't sure exactly which commit to use. These are based on top of Jason's latest hmm branch. Ralph Campbell (2): mm/hmm: hmm_range_fault() NULL pointer bug mm/hmm: hmm_range_fault() infinite loop mm/hmm.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) -- 2.20.1
Ralph Campbell
2019-Aug-23 22:17 UTC
[Nouveau] [PATCH 1/2] mm/hmm: hmm_range_fault() NULL pointer bug
Although hmm_range_fault() calls find_vma() to make sure that a vma exists before calling walk_page_range(), hmm_vma_walk_hole() can still be called with walk->vma == NULL if the start and end address are not contained within the vma range. hmm_range_fault() /* calls find_vma() but no range check */ walk_page_range() /* calls find_vma(), sets walk->vma = NULL */ __walk_page_range() walk_pgd_range() walk_p4d_range() walk_pud_range() hmm_vma_walk_hole() hmm_vma_walk_hole_() hmm_vma_do_fault() handle_mm_fault(vma=0) Signed-off-by: Ralph Campbell <rcampbell at nvidia.com> --- mm/hmm.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/mm/hmm.c b/mm/hmm.c index fc05c8fe78b4..29371485fe94 100644 --- a/mm/hmm.c +++ b/mm/hmm.c @@ -229,6 +229,9 @@ static int hmm_vma_do_fault(struct mm_walk *walk, unsigned long addr, struct vm_area_struct *vma = walk->vma; vm_fault_t ret; + if (!vma) + goto err; + if (hmm_vma_walk->flags & HMM_FAULT_ALLOW_RETRY) flags |= FAULT_FLAG_ALLOW_RETRY; if (write_fault) @@ -239,12 +242,14 @@ static int hmm_vma_do_fault(struct mm_walk *walk, unsigned long addr, /* Note, handle_mm_fault did up_read(&mm->mmap_sem)) */ return -EAGAIN; } - if (ret & VM_FAULT_ERROR) { - *pfn = range->values[HMM_PFN_ERROR]; - return -EFAULT; - } + if (ret & VM_FAULT_ERROR) + goto err; return -EBUSY; + +err: + *pfn = range->values[HMM_PFN_ERROR]; + return -EFAULT; } static int hmm_pfns_bad(unsigned long addr, -- 2.20.1
Ralph Campbell
2019-Aug-23 22:17 UTC
[Nouveau] [PATCH 2/2] mm/hmm: hmm_range_fault() infinite loop
Normally, callers to handle_mm_fault() are supposed to check the vma->vm_flags first. hmm_range_fault() checks for VM_READ but doesn't check for VM_WRITE if the caller requests a page to be faulted in with write permission (via the hmm_range.pfns[] value). If the vma is write protected, this can result in an infinite loop: hmm_range_fault() walk_page_range() ... hmm_vma_walk_hole() hmm_vma_walk_hole_() hmm_vma_do_fault() handle_mm_fault(FAULT_FLAG_WRITE) /* returns VM_FAULT_WRITE */ /* returns -EBUSY */ /* returns -EBUSY */ /* returns -EBUSY */ /* loops on -EBUSY and range->valid */ Prevent this by checking for vma->vm_flags & VM_WRITE before calling handle_mm_fault(). Signed-off-by: Ralph Campbell <rcampbell at nvidia.com> --- mm/hmm.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mm/hmm.c b/mm/hmm.c index 29371485fe94..4882b83aeccb 100644 --- a/mm/hmm.c +++ b/mm/hmm.c @@ -292,6 +292,9 @@ static int hmm_vma_walk_hole_(unsigned long addr, unsigned long end, hmm_vma_walk->last = addr; i = (addr - range->start) >> PAGE_SHIFT; + if (write_fault && walk->vma && !(walk->vma->vm_flags & VM_WRITE)) + return -EPERM; + for (; addr < end; addr += PAGE_SIZE, i++) { pfns[i] = range->values[HMM_PFN_NONE]; if (fault || write_fault) { -- 2.20.1
Christoph Hellwig
2019-Aug-24 22:37 UTC
[Nouveau] [PATCH 1/2] mm/hmm: hmm_range_fault() NULL pointer bug
On Fri, Aug 23, 2019 at 03:17:52PM -0700, Ralph Campbell wrote:> Although hmm_range_fault() calls find_vma() to make sure that a vma exists > before calling walk_page_range(), hmm_vma_walk_hole() can still be called > with walk->vma == NULL if the start and end address are not contained > within the vma range.Should we convert to walk_vma_range instead? Or keep walk_page_range but drop searching the vma ourselves? Except for that the patch looks good to me: Reviewed-by: Christoph Hellwig <hch at lst.de>
Christoph Hellwig
2019-Aug-24 22:39 UTC
[Nouveau] [PATCH 2/2] mm/hmm: hmm_range_fault() infinite loop
On Fri, Aug 23, 2019 at 03:17:53PM -0700, Ralph Campbell wrote:> Normally, callers to handle_mm_fault() are supposed to check the > vma->vm_flags first. hmm_range_fault() checks for VM_READ but doesn't > check for VM_WRITE if the caller requests a page to be faulted in > with write permission (via the hmm_range.pfns[] value). > If the vma is write protected, this can result in an infinite loop: > hmm_range_fault() > walk_page_range() > ... > hmm_vma_walk_hole() > hmm_vma_walk_hole_() > hmm_vma_do_fault() > handle_mm_fault(FAULT_FLAG_WRITE) > /* returns VM_FAULT_WRITE */ > /* returns -EBUSY */ > /* returns -EBUSY */ > /* returns -EBUSY */ > /* loops on -EBUSY and range->valid */ > Prevent this by checking for vma->vm_flags & VM_WRITE before calling > handle_mm_fault(). > > Signed-off-by: Ralph Campbell <rcampbell at nvidia.com>Looks good, Reviewed-by: Christoph Hellwig <hch at lst.de>
Jason Gunthorpe
2019-Aug-27 18:41 UTC
[Nouveau] [PATCH 2/2] mm/hmm: hmm_range_fault() infinite loop
On Fri, Aug 23, 2019 at 03:17:53PM -0700, Ralph Campbell wrote:> Signed-off-by: Ralph Campbell <rcampbell at nvidia.com> > mm/hmm.c | 3 +++ > 1 file changed, 3 insertions(+) > > diff --git a/mm/hmm.c b/mm/hmm.c > index 29371485fe94..4882b83aeccb 100644 > +++ b/mm/hmm.c > @@ -292,6 +292,9 @@ static int hmm_vma_walk_hole_(unsigned long addr, unsigned long end, > hmm_vma_walk->last = addr; > i = (addr - range->start) >> PAGE_SHIFT; > > + if (write_fault && walk->vma && !(walk->vma->vm_flags & VM_WRITE)) > + return -EPERM;Can walk->vma be NULL here? hmm_vma_do_fault() touches it unconditionally. Jason
Jason Gunthorpe
2019-Aug-27 22:48 UTC
[Nouveau] [PATCH 0/2] mm/hmm: two bug fixes for hmm_range_fault()
On Fri, Aug 23, 2019 at 03:17:51PM -0700, Ralph Campbell wrote:> I have been working on converting Jerome's hmm_dummy driver and self > tests into a stand-alone set of tests to be included in > tools/testing/selftests/vm and came across these two bug fixes in the > process. The tests aren't quite ready to be posted as a patch. > I'm posting the fixes now since I thought they shouldn't wait. > They should probably have a fixes line but with all the HMM changes, > I wasn't sure exactly which commit to use. > > These are based on top of Jason's latest hmm branch. > > Ralph Campbell (2): > mm/hmm: hmm_range_fault() NULL pointer bug > mm/hmm: hmm_range_fault() infinite loopApplied to hmm.git Thanks, Jason
Reasonably Related Threads
- [PATCH 2/2] mm/hmm: hmm_range_fault() infinite loop
- [PATCH 2/2] mm/hmm: hmm_range_fault() infinite loop
- [PATCH 1/2] mm/hmm: hmm_range_fault() NULL pointer bug
- [PATCH 0/2] mm/hmm: two bug fixes for hmm_range_fault()
- [PATCH 1/6] mm: always return EBUSY for invalid ranges in hmm_range_{fault, snapshot}