diff --git a/mm/gup.c b/mm/gup.c index ef29641671c7..76d222ccc3ff 100644 --- a/mm/gup.c +++ b/mm/gup.c @@ -1091,6 +1091,45 @@ static int check_vma_flags(struct vm_area_struct *vma, unsigned long gup_flags) return 0; } +/* + * This is "vma_lookup()", but with a warning if we would have + * historically expanded the stack in the GUP code. + */ +static struct vm_area_struct *gup_vma_lookup(struct mm_struct *mm, + unsigned long addr) +{ +#ifdef CONFIG_STACK_GROWSUP + return vma_lookup(mm, addr); +#else + static volatile unsigned long next_warn; + struct vm_area_struct *vma; + unsigned long now, next; + + vma = find_vma(mm, addr); + if (!vma || (addr >= vma->vm_start)) + return vma; + + /* Only warn for half-way relevant accesses */ + if (!(vma->vm_flags & VM_GROWSDOWN)) + return NULL; + if (vma->vm_start - addr > 65536) + return NULL; + + /* Let's not warn more than once an hour.. */ + now = jiffies; next = next_warn; + if (next && time_before(now, next)) + return NULL; + next_warn = now + 60*60*HZ; + + /* Let people know things may have changed. */ + pr_warn("GUP no longer grows the stack in %s (%d): %lx-%lx (%lx)\n", + current->comm, task_pid_nr(current), + vma->vm_start, vma->vm_end, addr); + dump_stack(); + return NULL; +#endif +} + /** * __get_user_pages() - pin user pages in memory * @mm: mm_struct of target mm @@ -1168,11 +1207,7 @@ static long __get_user_pages(struct mm_struct *mm, /* first iteration or cross vma bound */ if (!vma || start >= vma->vm_end) { - vma = find_vma(mm, start); - if (vma && (start < vma->vm_start)) { - WARN_ON_ONCE(vma->vm_flags & VM_GROWSDOWN); - vma = NULL; - } + vma = gup_vma_lookup(mm, start); if (!vma && in_gate_area(mm, start)) { ret = get_gate_page(mm, start & PAGE_MASK, gup_flags, &vma, @@ -1337,13 +1372,9 @@ int fixup_user_fault(struct mm_struct *mm, fault_flags |= FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE; retry: - vma = find_vma(mm, address); + vma = gup_vma_lookup(mm, address); if (!vma) return -EFAULT; - if (address < vma->vm_start ) { - WARN_ON_ONCE(vma->vm_flags & VM_GROWSDOWN); - return -EFAULT; - } if (!vma_permits_fault(vma, fault_flags)) return -EFAULT;