diff options
Diffstat (limited to 'mm/memory.c')
| -rw-r--r-- | mm/memory.c | 28 | 
1 files changed, 19 insertions, 9 deletions
diff --git a/mm/memory.c b/mm/memory.c index 9da8cab1b1b..ce22a250926 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -1410,6 +1410,13 @@ no_page_table:  	return page;  } +static inline int stack_guard_page(struct vm_area_struct *vma, unsigned long addr) +{ +	return (vma->vm_flags & VM_GROWSDOWN) && +		(vma->vm_start == addr) && +		!vma_stack_continue(vma->vm_prev, addr); +} +  /**   * __get_user_pages() - pin user pages in memory   * @tsk:	task_struct of target task @@ -1488,7 +1495,6 @@ int __get_user_pages(struct task_struct *tsk, struct mm_struct *mm,  		vma = find_extend_vma(mm, start);  		if (!vma && in_gate_area(mm, start)) {  			unsigned long pg = start & PAGE_MASK; -			struct vm_area_struct *gate_vma = get_gate_vma(mm);  			pgd_t *pgd;  			pud_t *pud;  			pmd_t *pmd; @@ -1513,10 +1519,11 @@ int __get_user_pages(struct task_struct *tsk, struct mm_struct *mm,  				pte_unmap(pte);  				return i ? : -EFAULT;  			} +			vma = get_gate_vma(mm);  			if (pages) {  				struct page *page; -				page = vm_normal_page(gate_vma, start, *pte); +				page = vm_normal_page(vma, start, *pte);  				if (!page) {  					if (!(gup_flags & FOLL_DUMP) &&  					     is_zero_pfn(pte_pfn(*pte))) @@ -1530,12 +1537,7 @@ int __get_user_pages(struct task_struct *tsk, struct mm_struct *mm,  				get_page(page);  			}  			pte_unmap(pte); -			if (vmas) -				vmas[i] = gate_vma; -			i++; -			start += PAGE_SIZE; -			nr_pages--; -			continue; +			goto next_page;  		}  		if (!vma || @@ -1549,6 +1551,13 @@ int __get_user_pages(struct task_struct *tsk, struct mm_struct *mm,  			continue;  		} +		/* +		 * If we don't actually want the page itself, +		 * and it's the stack guard page, just skip it. +		 */ +		if (!pages && stack_guard_page(vma, start)) +			goto next_page; +  		do {  			struct page *page;  			unsigned int foll_flags = gup_flags; @@ -1631,6 +1640,7 @@ int __get_user_pages(struct task_struct *tsk, struct mm_struct *mm,  				flush_anon_page(vma, page, start);  				flush_dcache_page(page);  			} +next_page:  			if (vmas)  				vmas[i] = vma;  			i++; @@ -3678,7 +3688,7 @@ static int __access_remote_vm(struct task_struct *tsk, struct mm_struct *mm,  			 */  #ifdef CONFIG_HAVE_IOREMAP_PROT  			vma = find_vma(mm, addr); -			if (!vma) +			if (!vma || vma->vm_start > addr)  				break;  			if (vma->vm_ops && vma->vm_ops->access)  				ret = vma->vm_ops->access(vma, addr, buf,  | 
