[2/2] mm/hmm: hmm_range_fault() infinite loop

Submitted by Ralph Campbell on Aug. 23, 2019, 10:17 p.m.

Details

Message ID 20190823221753.2514-3-rcampbell@nvidia.com
State New
Headers show
Series "mm/hmm: two bug fixes for hmm_range_fault()" ( rev: 1 ) in DRI devel

Not browsing as part of any series.

Commit Message

Ralph Campbell Aug. 23, 2019, 10:17 p.m.
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@nvidia.com>
---
 mm/hmm.c | 3 +++
 1 file changed, 3 insertions(+)

Patch hide | download patch | download mbox

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) {

Comments

On Fri, Aug 23, 2019 at 03:17:53PM -0700, Ralph Campbell wrote:

> Signed-off-by: Ralph Campbell <rcampbell@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
On 8/27/19 11:41 AM, Jason Gunthorpe wrote:
> On Fri, Aug 23, 2019 at 03:17:53PM -0700, Ralph Campbell wrote:
> 
>> Signed-off-by: Ralph Campbell <rcampbell@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
> 
walk->vma can be NULL. hmm_vma_do_fault() no longer touches it
unconditionally, that is what the preceding patch fixes.
I suppose I could change hmm_vma_walk_hole_() to check for NULL
and fill in the pfns[] array, I just chose to handle it in
hmm_vma_do_fault().
On Tue, Aug 27, 2019 at 01:16:13PM -0700, Ralph Campbell wrote:
> 
> On 8/27/19 11:41 AM, Jason Gunthorpe wrote:
> > On Fri, Aug 23, 2019 at 03:17:53PM -0700, Ralph Campbell wrote:
> > 
> > > Signed-off-by: Ralph Campbell <rcampbell@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
> > 
> walk->vma can be NULL. hmm_vma_do_fault() no longer touches it
> unconditionally, that is what the preceding patch fixes.
> I suppose I could change hmm_vma_walk_hole_() to check for NULL
> and fill in the pfns[] array, I just chose to handle it in
> hmm_vma_do_fault().

Okay, yes maybe long term it would be clearer to do the vma null check
closer to the start of the callback, but this is a good enough bug fix

Reviewed-by: Jason Gunthorpe <jgg@mellanox.com>

Jason