diff mbox series

[OpenWrt-Devel] kernel: backport MIPS: Bounds check virt_addr_valid

Message ID 20190609135020.11156-1-hauke@hauke-m.de
State Superseded
Delegated to: Hauke Mehrtens
Headers show
Series [OpenWrt-Devel] kernel: backport MIPS: Bounds check virt_addr_valid | expand

Commit Message

Hauke Mehrtens June 9, 2019, 1:50 p.m. UTC
This fixes bugs in the copy_{to,from}_user hardening like this:

usercopy: kernel memory exposure attempt detected from c0d173c1 (kmalloc-256) (71 bytes)
Kernel bug detected[#1]:

Fixes: 9b1239451d6 ("Kernel: Activate CONFIG_HARDENED_USERCOPY")
Fixes: FS#2305 and FS#2297
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
---
 ....2-MIPS-Bounds-check-virt_addr_valid.patch | 70 +++++++++++++++++++
 ....2-MIPS-Bounds-check-virt_addr_valid.patch | 70 +++++++++++++++++++
 2 files changed, 140 insertions(+)
 create mode 100644 target/linux/generic/backport-4.14/430-v5.2-MIPS-Bounds-check-virt_addr_valid.patch
 create mode 100644 target/linux/generic/backport-4.19/430-v5.2-MIPS-Bounds-check-virt_addr_valid.patch
diff mbox series

Patch

diff --git a/target/linux/generic/backport-4.14/430-v5.2-MIPS-Bounds-check-virt_addr_valid.patch b/target/linux/generic/backport-4.14/430-v5.2-MIPS-Bounds-check-virt_addr_valid.patch
new file mode 100644
index 0000000000..028e84a691
--- /dev/null
+++ b/target/linux/generic/backport-4.14/430-v5.2-MIPS-Bounds-check-virt_addr_valid.patch
@@ -0,0 +1,70 @@ 
+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: [PATCH] MIPS: Bounds check virt_addr_valid
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+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>
+---
+ 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);
diff --git a/target/linux/generic/backport-4.19/430-v5.2-MIPS-Bounds-check-virt_addr_valid.patch b/target/linux/generic/backport-4.19/430-v5.2-MIPS-Bounds-check-virt_addr_valid.patch
new file mode 100644
index 0000000000..028e84a691
--- /dev/null
+++ b/target/linux/generic/backport-4.19/430-v5.2-MIPS-Bounds-check-virt_addr_valid.patch
@@ -0,0 +1,70 @@ 
+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: [PATCH] MIPS: Bounds check virt_addr_valid
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+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>
+---
+ 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);