Re: [PATCHv3 1/2] x86/mm: Prevent non-MAP_FIXED mapping across DEFAULT_MAP_WINDOW border

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Wed 15-11-17 17:36:06, Kirill A. Shutemov wrote:
> In case of 5-level paging, the kernel does not place any mapping above
> 47-bit, unless userspace explicitly asks for it.
> 
> Userspace can request an allocation from the full address space by
> specifying the mmap address hint above 47-bit.
> 
> Nicholas noticed that the current implementation violates this interface:
> 
>   If user space requests a mapping at the end of the 47-bit address space
>   with a length which causes the mapping to cross the 47-bit border
>   (DEFAULT_MAP_WINDOW), then the vma is partially in the address space
>   below and above.
> 
> Sanity check the mmap address hint so that start and end of the resulting
> vma are on the same side of the 47-bit border. If that's not the case fall
> back to the code path which ignores the address hint and allocate from the
> regular address space below 47-bit.
> 
> [ tglx: Moved the address check to a function and massaged comment and
>   	changelog ]
> 
> Reported-by: Nicholas Piggin <npiggin@xxxxxxxxx>
> Signed-off-by: Kirill A. Shutemov <kirill.shutemov@xxxxxxxxxxxxxxx>
> Signed-off-by: Thomas Gleixner <tglx@xxxxxxxxxxxxx>

FWIW
Acked-by: Michal Hocko <mhocko@xxxxxxxx>

> ---
>  arch/x86/include/asm/elf.h   |  1 +
>  arch/x86/kernel/sys_x86_64.c | 10 +++++++---
>  arch/x86/mm/hugetlbpage.c    | 11 ++++++++---
>  arch/x86/mm/mmap.c           | 46 ++++++++++++++++++++++++++++++++++++++++++++
>  4 files changed, 62 insertions(+), 6 deletions(-)
> 
> diff --git a/arch/x86/include/asm/elf.h b/arch/x86/include/asm/elf.h
> index 3a091cea36c5..0d157d2a1e2a 100644
> --- a/arch/x86/include/asm/elf.h
> +++ b/arch/x86/include/asm/elf.h
> @@ -309,6 +309,7 @@ static inline int mmap_is_ia32(void)
>  extern unsigned long task_size_32bit(void);
>  extern unsigned long task_size_64bit(int full_addr_space);
>  extern unsigned long get_mmap_base(int is_legacy);
> +extern bool mmap_address_hint_valid(unsigned long addr, unsigned long len);
>  
>  #ifdef CONFIG_X86_32
>  
> diff --git a/arch/x86/kernel/sys_x86_64.c b/arch/x86/kernel/sys_x86_64.c
> index a63fe77b3217..676774b9bb8d 100644
> --- a/arch/x86/kernel/sys_x86_64.c
> +++ b/arch/x86/kernel/sys_x86_64.c
> @@ -188,6 +188,7 @@ arch_get_unmapped_area_topdown(struct file *filp, const unsigned long addr0,
>  	if (len > TASK_SIZE)
>  		return -ENOMEM;
>  
> +	/* No address checking. See comment at mmap_address_hint_valid() */
>  	if (flags & MAP_FIXED)
>  		return addr;
>  
> @@ -197,12 +198,15 @@ arch_get_unmapped_area_topdown(struct file *filp, const unsigned long addr0,
>  
>  	/* requesting a specific address */
>  	if (addr) {
> -		addr = PAGE_ALIGN(addr);
> +		addr &= PAGE_MASK;
> +		if (!mmap_address_hint_valid(addr, len))
> +			goto get_unmapped_area;
> +
>  		vma = find_vma(mm, addr);
> -		if (TASK_SIZE - len >= addr &&
> -				(!vma || addr + len <= vm_start_gap(vma)))
> +		if (!vma || addr + len <= vm_start_gap(vma))
>  			return addr;
>  	}
> +get_unmapped_area:
>  
>  	info.flags = VM_UNMAPPED_AREA_TOPDOWN;
>  	info.length = len;
> diff --git a/arch/x86/mm/hugetlbpage.c b/arch/x86/mm/hugetlbpage.c
> index 8ae0000cbdb3..00b296617ca4 100644
> --- a/arch/x86/mm/hugetlbpage.c
> +++ b/arch/x86/mm/hugetlbpage.c
> @@ -158,6 +158,7 @@ hugetlb_get_unmapped_area(struct file *file, unsigned long addr,
>  	if (len > TASK_SIZE)
>  		return -ENOMEM;
>  
> +	/* No address checking. See comment at mmap_address_hint_valid() */
>  	if (flags & MAP_FIXED) {
>  		if (prepare_hugepage_range(file, addr, len))
>  			return -EINVAL;
> @@ -165,12 +166,16 @@ hugetlb_get_unmapped_area(struct file *file, unsigned long addr,
>  	}
>  
>  	if (addr) {
> -		addr = ALIGN(addr, huge_page_size(h));
> +		addr &= huge_page_mask(h);
> +		if (!mmap_address_hint_valid(addr, len))
> +			goto get_unmapped_area;
> +
>  		vma = find_vma(mm, addr);
> -		if (TASK_SIZE - len >= addr &&
> -		    (!vma || addr + len <= vm_start_gap(vma)))
> +		if (!vma || addr + len <= vm_start_gap(vma))
>  			return addr;
>  	}
> +
> +get_unmapped_area:
>  	if (mm->get_unmapped_area == arch_get_unmapped_area)
>  		return hugetlb_get_unmapped_area_bottomup(file, addr, len,
>  				pgoff, flags);
> diff --git a/arch/x86/mm/mmap.c b/arch/x86/mm/mmap.c
> index a99679826846..62285fe77b0f 100644
> --- a/arch/x86/mm/mmap.c
> +++ b/arch/x86/mm/mmap.c
> @@ -174,3 +174,49 @@ const char *arch_vma_name(struct vm_area_struct *vma)
>  		return "[mpx]";
>  	return NULL;
>  }
> +
> +/**
> + * mmap_address_hint_valid - Validate the address hint of mmap
> + * @addr:	Address hint
> + * @len:	Mapping length
> + *
> + * Check whether @addr and @addr + @len result in a valid mapping.
> + *
> + * On 32bit this only checks whether @addr + @len is <= TASK_SIZE.
> + *
> + * On 64bit with 5-level page tables another sanity check is required
> + * because mappings requested by mmap(@addr, 0) which cross the 47-bit
> + * virtual address boundary can cause the following theoretical issue:
> + *
> + *  An application calls mmap(addr, 0), i.e. without MAP_FIXED, where @addr
> + *  is below the border of the 47-bit address space and @addr + @len is
> + *  above the border.
> + *
> + *  With 4-level paging this request succeeds, but the resulting mapping
> + *  address will always be within the 47-bit virtual address space, because
> + *  the hint address does not result in a valid mapping and is
> + *  ignored. Hence applications which are not prepared to handle virtual
> + *  addresses above 47-bit work correctly.
> + *
> + *  With 5-level paging this request would be granted and result in a
> + *  mapping which crosses the border of the 47-bit virtual address
> + *  space. If the application cannot handle addresses above 47-bit this
> + *  will lead to misbehaviour and hard to diagnose failures.
> + *
> + * Therefore ignore address hints which would result in a mapping crossing
> + * the 47-bit virtual address boundary.
> + *
> + * Note, that in the same scenario with MAP_FIXED the behaviour is
> + * different. The request with @addr < 47-bit and @addr + @len > 47-bit
> + * fails on a 4-level paging machine but succeeds on a 5-level paging
> + * machine. It is reasonable to expect that an application does not rely on
> + * the failure of such a fixed mapping request, so the restriction is not
> + * applied.
> + */
> +bool mmap_address_hint_valid(unsigned long addr, unsigned long len)
> +{
> +	if (TASK_SIZE - len < addr)
> +		return false;
> +
> +	return (addr > DEFAULT_MAP_WINDOW) == (addr + len > DEFAULT_MAP_WINDOW);
> +}
> -- 
> 2.15.0
> 
> --
> To unsubscribe, send a message with 'unsubscribe linux-mm' in
> the body to majordomo@xxxxxxxxx.  For more info on Linux MM,
> see: http://www.linux-mm.org/ .
> Don't email: <a href=mailto:"dont@xxxxxxxxx";> email@xxxxxxxxx </a>

-- 
Michal Hocko
SUSE Labs

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@xxxxxxxxx.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@xxxxxxxxx";> email@xxxxxxxxx </a>



[Index of Archives]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [IETF Annouce]     [Bugtraq]     [Linux OMAP]     [Linux MIPS]     [eCos]     [Asterisk Internet PBX]     [Linux API]
  Powered by Linux