mirror of
https://github.com/raspberrypi/linux.git
synced 2025-12-07 18:40:10 +00:00
[ Upstream commit5330367fa3] After we ALIGN up the address we need to make sure we didn't overflow and resulted in zero address. In that case, we need to make sure that the returned address is greater than mmap_min_addr. This fixes selftest va_128TBswitch --run-hugetlb reporting failures when run as non root user for mmap(-1, MAP_HUGETLB) The bug is that a non-root user requesting address -1 will be given address 0 which will then fail, whereas they should have been given something else that would have succeeded. We also avoid the first mmap(-1, MAP_HUGETLB) returning NULL address as mmap address with this change. So we think this is not a security issue, because it only affects whether we choose an address below mmap_min_addr, not whether we actually allow that address to be mapped. ie. there are existing capability checks to prevent a user mapping below mmap_min_addr and those will still be honoured even without this fix. Fixes:484837601d("powerpc/mm: Add radix support for hugetlb") Reviewed-by: Laurent Dufour <ldufour@linux.vnet.ibm.com> Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.ibm.com> Signed-off-by: Michael Ellerman <mpe@ellerman.id.au> Signed-off-by: Sasha Levin <sashal@kernel.org>
94 lines
2.5 KiB
C
94 lines
2.5 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
#include <linux/mm.h>
|
|
#include <linux/hugetlb.h>
|
|
#include <linux/security.h>
|
|
#include <asm/pgtable.h>
|
|
#include <asm/pgalloc.h>
|
|
#include <asm/cacheflush.h>
|
|
#include <asm/machdep.h>
|
|
#include <asm/mman.h>
|
|
#include <asm/tlb.h>
|
|
|
|
void radix__flush_hugetlb_page(struct vm_area_struct *vma, unsigned long vmaddr)
|
|
{
|
|
int psize;
|
|
struct hstate *hstate = hstate_file(vma->vm_file);
|
|
|
|
psize = hstate_get_psize(hstate);
|
|
radix__flush_tlb_page_psize(vma->vm_mm, vmaddr, psize);
|
|
}
|
|
|
|
void radix__local_flush_hugetlb_page(struct vm_area_struct *vma, unsigned long vmaddr)
|
|
{
|
|
int psize;
|
|
struct hstate *hstate = hstate_file(vma->vm_file);
|
|
|
|
psize = hstate_get_psize(hstate);
|
|
radix__local_flush_tlb_page_psize(vma->vm_mm, vmaddr, psize);
|
|
}
|
|
|
|
void radix__flush_hugetlb_tlb_range(struct vm_area_struct *vma, unsigned long start,
|
|
unsigned long end)
|
|
{
|
|
int psize;
|
|
struct hstate *hstate = hstate_file(vma->vm_file);
|
|
|
|
psize = hstate_get_psize(hstate);
|
|
radix__flush_tlb_range_psize(vma->vm_mm, start, end, psize);
|
|
}
|
|
|
|
/*
|
|
* A vairant of hugetlb_get_unmapped_area doing topdown search
|
|
* FIXME!! should we do as x86 does or non hugetlb area does ?
|
|
* ie, use topdown or not based on mmap_is_legacy check ?
|
|
*/
|
|
unsigned long
|
|
radix__hugetlb_get_unmapped_area(struct file *file, unsigned long addr,
|
|
unsigned long len, unsigned long pgoff,
|
|
unsigned long flags)
|
|
{
|
|
struct mm_struct *mm = current->mm;
|
|
struct vm_area_struct *vma;
|
|
struct hstate *h = hstate_file(file);
|
|
int fixed = (flags & MAP_FIXED);
|
|
unsigned long high_limit;
|
|
struct vm_unmapped_area_info info;
|
|
|
|
high_limit = DEFAULT_MAP_WINDOW;
|
|
if (addr >= high_limit || (fixed && (addr + len > high_limit)))
|
|
high_limit = TASK_SIZE;
|
|
|
|
if (len & ~huge_page_mask(h))
|
|
return -EINVAL;
|
|
if (len > high_limit)
|
|
return -ENOMEM;
|
|
|
|
if (fixed) {
|
|
if (addr > high_limit - len)
|
|
return -ENOMEM;
|
|
if (prepare_hugepage_range(file, addr, len))
|
|
return -EINVAL;
|
|
return addr;
|
|
}
|
|
|
|
if (addr) {
|
|
addr = ALIGN(addr, huge_page_size(h));
|
|
vma = find_vma(mm, addr);
|
|
if (high_limit - len >= addr && addr >= mmap_min_addr &&
|
|
(!vma || addr + len <= vm_start_gap(vma)))
|
|
return addr;
|
|
}
|
|
/*
|
|
* We are always doing an topdown search here. Slice code
|
|
* does that too.
|
|
*/
|
|
info.flags = VM_UNMAPPED_AREA_TOPDOWN;
|
|
info.length = len;
|
|
info.low_limit = max(PAGE_SIZE, mmap_min_addr);
|
|
info.high_limit = mm->mmap_base + (high_limit - DEFAULT_MAP_WINDOW);
|
|
info.align_mask = PAGE_MASK & ~huge_page_mask(h);
|
|
info.align_offset = 0;
|
|
|
|
return vm_unmapped_area(&info);
|
|
}
|