| From 074a1e1167afd82c26f6d03a9a8b997d564bb241 Mon Sep 17 00:00:00 2001 |
| From: Paul Burton <paul.burton@mips.com> |
| Date: Tue, 28 May 2019 17:05:03 +0000 |
| Subject: MIPS: Bounds check virt_addr_valid |
| MIME-Version: 1.0 |
| Content-Type: text/plain; charset=UTF-8 |
| Content-Transfer-Encoding: 8bit |
| |
| From: Paul Burton <paul.burton@mips.com> |
| |
| commit 074a1e1167afd82c26f6d03a9a8b997d564bb241 upstream. |
| |
| The virt_addr_valid() function is meant to return true iff |
| virt_to_page() will return a valid struct page reference. This is true |
| iff the address provided is found within the unmapped address range |
| between PAGE_OFFSET & MAP_BASE, but we don't currently check for that |
| condition. Instead we simply mask the address to obtain what will be a |
| physical address if the virtual address is indeed in the desired range, |
| shift it to form a PFN & then call pfn_valid(). This can incorrectly |
| return true if called with a virtual address which, after masking, |
| happens to form a physical address corresponding to a valid PFN. |
| |
| For example we may vmalloc an address in the kernel mapped region |
| starting a MAP_BASE & obtain the virtual address: |
| |
| addr = 0xc000000000002000 |
| |
| When masked by virt_to_phys(), which uses __pa() & in turn CPHYSADDR(), |
| we obtain the following (bogus) physical address: |
| |
| addr = 0x2000 |
| |
| In a common system with PHYS_OFFSET=0 this will correspond to a valid |
| struct page which should really be accessed by virtual address |
| PAGE_OFFSET+0x2000, causing virt_addr_valid() to incorrectly return 1 |
| indicating that the original address corresponds to a struct page. |
| |
| This is equivalent to the ARM64 change made in commit ca219452c6b8 |
| ("arm64: Correctly bounds check virt_addr_valid"). |
| |
| This fixes fallout when hardened usercopy is enabled caused by the |
| related commit 517e1fbeb65f ("mm/usercopy: Drop extra |
| is_vmalloc_or_module() check") which removed a check for the vmalloc |
| range that was present from the introduction of the hardened usercopy |
| feature. |
| |
| Signed-off-by: Paul Burton <paul.burton@mips.com> |
| References: ca219452c6b8 ("arm64: Correctly bounds check virt_addr_valid") |
| References: 517e1fbeb65f ("mm/usercopy: Drop extra is_vmalloc_or_module() check") |
| Reported-by: Julien Cristau <jcristau@debian.org> |
| Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org> |
| Tested-by: YunQiang Su <ysu@wavecomp.com> |
| URL: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=929366 |
| Cc: stable@vger.kernel.org # v4.12+ |
| Cc: linux-mips@vger.kernel.org |
| Cc: Yunqiang Su <ysu@wavecomp.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| arch/mips/mm/mmap.c | 5 +++++ |
| 1 file changed, 5 insertions(+) |
| |
| --- a/arch/mips/mm/mmap.c |
| +++ b/arch/mips/mm/mmap.c |
| @@ -203,6 +203,11 @@ unsigned long arch_randomize_brk(struct |
| |
| int __virt_addr_valid(const volatile void *kaddr) |
| { |
| + unsigned long vaddr = (unsigned long)vaddr; |
| + |
| + if ((vaddr < PAGE_OFFSET) || (vaddr >= MAP_BASE)) |
| + return 0; |
| + |
| return pfn_valid(PFN_DOWN(virt_to_phys(kaddr))); |
| } |
| EXPORT_SYMBOL_GPL(__virt_addr_valid); |