This problem reported by Clement LE GOFFIC manifest when using CONFIG_KASAN_IN_VMALLOC and VMAP_STACK: https://lore.kernel.org/linux-arm-kernel/a1a1d062-f3a2-4d05-9836-3b098de9db6...
After some analysis it seems we are missing to sync the VMALLOC shadow memory in top level PGD to all CPUs.
Add some code to perform this sync, and the bug appears to go away.
As suggested by Ard, also perform a dummy read from the shadow memory of the new VMAP_STACK in the low level assembly.
Signed-off-by: Linus Walleij linus.walleij@linaro.org --- Changes in v4: - Since Kasan is not using header stubs, it is necessary to avoid kasan_*() calls using ifdef when compiling without KASAN. - Lift a line aligning the end of vmalloc from Melon Liu's very similar patch so we have feature parity, credit Melon as co-developer. - Include the atomic_read_acquire() patch in the series due to context requirements. - Verify that the after the patch the kernel still builds and boots without Kasan. - Link to v3: https://lore.kernel.org/r/20241017-arm-kasan-vmalloc-crash-v3-0-d2a34cd5b663...
Changes in v3: - Collect Mark Rutlands ACK on patch 1 - Change the simplified assembly add r2, ip, lsr #n to the canonical add r2, r2, ip, lsr #n in patch 2. - Link to v2: https://lore.kernel.org/r/20241016-arm-kasan-vmalloc-crash-v2-0-0a52fd086eef...
Changes in v2: - Implement the two helper functions suggested by Russell making the KASAN PGD copying less messy. - Link to v1: https://lore.kernel.org/r/20241015-arm-kasan-vmalloc-crash-v1-0-dbb23592ca83...
--- Linus Walleij (3): ARM: ioremap: Sync PGDs for VMALLOC shadow ARM: entry: Do a dummy read from VMAP shadow mm: Pair atomic_set_release() with _read_acquire()
arch/arm/kernel/entry-armv.S | 8 ++++++++ arch/arm/mm/ioremap.c | 35 ++++++++++++++++++++++++++++++----- 2 files changed, 38 insertions(+), 5 deletions(-) --- base-commit: 9852d85ec9d492ebef56dc5f229416c925758edc change-id: 20241015-arm-kasan-vmalloc-crash-fcbd51416457
Best regards,
When sync:ing the VMALLOC area to other CPUs, make sure to also sync the KASAN shadow memory for the VMALLOC area, so that we don't get stale entries for the shadow memory in the top level PGD.
Since we are now copying PGDs in two instances, create a helper function named memcpy_pgd() to do the actual copying, and create a helper to map the addresses of VMALLOC_START and VMALLOC_END into the corresponding shadow memory.
Cc: stable@vger.kernel.org Fixes: 565cbaad83d8 ("ARM: 9202/1: kasan: support CONFIG_KASAN_VMALLOC") Link: https://lore.kernel.org/linux-arm-kernel/a1a1d062-f3a2-4d05-9836-3b098de9db6... Reported-by: Clement LE GOFFIC clement.legoffic@foss.st.com Suggested-by: Mark Rutland mark.rutland@arm.com Suggested-by: Russell King (Oracle) linux@armlinux.org.uk Acked-by: Mark Rutland mark.rutland@arm.com Co-developed-by: Melon Liu melon1335@163.com Signed-off-by: Linus Walleij linus.walleij@linaro.org --- arch/arm/mm/ioremap.c | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-)
diff --git a/arch/arm/mm/ioremap.c b/arch/arm/mm/ioremap.c index 794cfea9f9d4..ff555823cceb 100644 --- a/arch/arm/mm/ioremap.c +++ b/arch/arm/mm/ioremap.c @@ -23,6 +23,7 @@ */ #include <linux/module.h> #include <linux/errno.h> +#include <linux/kasan.h> #include <linux/mm.h> #include <linux/vmalloc.h> #include <linux/io.h> @@ -115,16 +116,40 @@ int ioremap_page(unsigned long virt, unsigned long phys, } EXPORT_SYMBOL(ioremap_page);
+#ifdef CONFIG_KASAN +static unsigned long arm_kasan_mem_to_shadow(unsigned long addr) +{ + return (unsigned long)kasan_mem_to_shadow((void *)addr); +} +#else +static unsigned long arm_kasan_mem_to_shadow(unsigned long addr) +{ + return 0; +} +#endif + +static void memcpy_pgd(struct mm_struct *mm, unsigned long start, + unsigned long end) +{ + end = ALIGN(end, PGDIR_SIZE); + memcpy(pgd_offset(mm, start), pgd_offset_k(start), + sizeof(pgd_t) * (pgd_index(end) - pgd_index(start))); +} + void __check_vmalloc_seq(struct mm_struct *mm) { int seq;
do { seq = atomic_read(&init_mm.context.vmalloc_seq); - memcpy(pgd_offset(mm, VMALLOC_START), - pgd_offset_k(VMALLOC_START), - sizeof(pgd_t) * (pgd_index(VMALLOC_END) - - pgd_index(VMALLOC_START))); + memcpy_pgd(mm, VMALLOC_START, VMALLOC_END); + if (IS_ENABLED(CONFIG_KASAN_VMALLOC)) { + unsigned long start = + arm_kasan_mem_to_shadow(VMALLOC_START); + unsigned long end = + arm_kasan_mem_to_shadow(VMALLOC_END); + memcpy_pgd(mm, start, end); + } /* * Use a store-release so that other CPUs that observe the * counter's new value are guaranteed to see the results of the
When switching task, in addition to a dummy read from the new VMAP stack, also do a dummy read from the VMAP stack's corresponding KASAN shadow memory to sync things up in the new MM context.
Cc: stable@vger.kernel.org Fixes: a1c510d0adc6 ("ARM: implement support for vmap'ed stacks") Link: https://lore.kernel.org/linux-arm-kernel/a1a1d062-f3a2-4d05-9836-3b098de9db6... Reported-by: Clement LE GOFFIC clement.legoffic@foss.st.com Suggested-by: Ard Biesheuvel ardb@kernel.org Signed-off-by: Linus Walleij linus.walleij@linaro.org --- arch/arm/kernel/entry-armv.S | 8 ++++++++ 1 file changed, 8 insertions(+)
diff --git a/arch/arm/kernel/entry-armv.S b/arch/arm/kernel/entry-armv.S index 1dfae1af8e31..ef6a657c8d13 100644 --- a/arch/arm/kernel/entry-armv.S +++ b/arch/arm/kernel/entry-armv.S @@ -25,6 +25,7 @@ #include <asm/tls.h> #include <asm/system_info.h> #include <asm/uaccess-asm.h> +#include <asm/kasan_def.h>
#include "entry-header.S" #include <asm/probes.h> @@ -561,6 +562,13 @@ ENTRY(__switch_to) @ entries covering the vmalloc region. @ ldr r2, [ip] +#ifdef CONFIG_KASAN_VMALLOC + @ Also dummy read from the KASAN shadow memory for the new stack if we + @ are using KASAN + mov_l r2, KASAN_SHADOW_OFFSET + add r2, r2, ip, lsr #KASAN_SHADOW_SCALE_SHIFT + ldr r2, [r2] +#endif #endif
@ When CONFIG_THREAD_INFO_IN_TASK=n, the update of SP itself is what
The code for syncing vmalloc memory PGD pointers is using atomic_read() in pair with atomic_set_release() but the proper pairing is atomic_read_acquire() paired with atomic_set_release().
This is done to clearly instruct the compiler to not reorder the memcpy() or similar calls inside the section so that we do not observe changes to init_mm. memcpy() calls should be identified by the compiler as having unpredictable side effects, but let's try to be on the safe side.
Cc: stable@vger.kernel.org Fixes: d31e23aff011 ("ARM: mm: make vmalloc_seq handling SMP safe") Suggested-by: Mark Rutland mark.rutland@arm.com Signed-off-by: Linus Walleij linus.walleij@linaro.org --- arch/arm/mm/ioremap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm/mm/ioremap.c b/arch/arm/mm/ioremap.c index ff555823cceb..89f1c97f3079 100644 --- a/arch/arm/mm/ioremap.c +++ b/arch/arm/mm/ioremap.c @@ -141,7 +141,7 @@ void __check_vmalloc_seq(struct mm_struct *mm) int seq;
do { - seq = atomic_read(&init_mm.context.vmalloc_seq); + seq = atomic_read_acquire(&init_mm.context.vmalloc_seq); memcpy_pgd(mm, VMALLOC_START, VMALLOC_END); if (IS_ENABLED(CONFIG_KASAN_VMALLOC)) { unsigned long start =
On Mon, Oct 21, 2024 at 3:03 PM Linus Walleij linus.walleij@linaro.org wrote:
This problem reported by Clement LE GOFFIC manifest when using CONFIG_KASAN_IN_VMALLOC and VMAP_STACK: https://lore.kernel.org/linux-arm-kernel/a1a1d062-f3a2-4d05-9836-3b098de9db6...
After some analysis it seems we are missing to sync the VMALLOC shadow memory in top level PGD to all CPUs.
Add some code to perform this sync, and the bug appears to go away.
As suggested by Ard, also perform a dummy read from the shadow memory of the new VMAP_STACK in the low level assembly.
Signed-off-by: Linus Walleij linus.walleij@linaro.org
As these are regressions that need to go in as fixes I'm putting them into Russell's patch tracker now.
The 9427/1 patch: https://www.arm.linux.org.uk/developer/patches/viewpatch.php?id=9427/1
Need to be avoided as it causes build regressions. Patch 1/3 supersedes it.
Yours, Linus Walleij
linux-stable-mirror@lists.linaro.org