"Mukesh Kumar Chaurasiya (IBM)" <[email protected]> writes:
Hi, I fed this patch to an AI for review, and the review results are included below. Please take a look. From my side, I agree with all of the points raised by AI in the review, and I think they all need to be addressed. There is also a diff with the suggested changes at the very end of this mail. > powerpc unconditionally selects GENERIC_ENTRY. The GENERIC_ENTRY > infrastructure relies on the compiler emitting __asan_mem*() calls at > instrumented mem*() sites rather than plain memset/memcpy/memmove, so > that entry/exit paths calling those functions are not instrumented. > > [ ... ] > > When GENERIC_ENTRY is set, both guards suppress the C wrappers for > memset/memcpy/memmove and the __underlying_mem*() redirections. This > is only safe when the compiler supports the prefixed __asan_mem*() > intrinsics. On older toolchains (e.g. GCC 9) that lack this support, > plain mem*() calls from instrumented code fall through to the raw > assembly implementations in mem_64.S / copy_32.S, completely bypassing > the KASAN shadow check. > > Other arches with GENERIC_ENTRY (x86, s390, loongarch, riscv) do not > hit this because their CI toolchains are always new enough to support > the prefix flag. > > [ ... ] > > Reported-by: Venkat Rao Bagalkote <[email protected]> > Closes: > https://lore.kernel.org/all/[email protected] > Tested-by: Venkat Rao Bagalkote <[email protected]> > Signed-off-by: Mukesh Kumar Chaurasiya (IBM) <[email protected]> The thread in the Closes: link is about an early boot hang, but the commit message only describes mem*() calls bypassing the shadow check. Bypassed checks would lose coverage, not hang the machine. Is the mechanism of the hang understood? Looking at the state before this commit, the !CC_HAS_KASAN_MEMINTRINSIC_PREFIX macros that bee25f97ad24 ("powerpc: Enable GENERIC_ENTRY feature") added to asm/kasan.h emit two ELFv2 global entry points back to back: #define _GLOBAL_TOC_KASAN(fn) _GLOBAL_TOC(fn); _GLOBAL_TOC(__##fn) With the _GLOBAL_TOC() definition from asm/ppc_asm.h that expands, for memcpy_64.S, to: memcpy: 0: addis r2,r12,(.TOC.-0b)@ha addi r2,r2,(.TOC.-0b)@l .localentry memcpy,.-memcpy <- local entry is memcpy+8 __memcpy: 0: addis r2,r12,(.TOC.-0b)@ha <- this is memcpy+8 addi r2,r2,(.TOC.-0b)@l .localentry __memcpy,.-__memcpy Assembling exactly that for powerpc64le gives memcpy st_other 0x60 (local entry offset 8), and memcpy+8 is the __memcpy TOC prologue. Every same-TOC caller of memcpy() or memmove() is resolved by the linker to the local entry, so it lands on that second prologue with r12 holding whatever the caller left there, and returns with r2 pointing at garbage. Same-TOC callers do not reload r2 after the call. With GENERIC_ENTRY, mm/kasan/shadow.c no longer provides memcpy(), so on a toolchain without the prefix parameter every instrumented file calls the memcpy symbol directly and hits this. That matches an early hang that only shows up with GCC 9. This commit makes the hang go away because the dual-entry macro is deleted, but the commit message attributes the fix to something else. Could the message describe the r2 corruption, and since this repairs a regression from the GENERIC_ENTRY conversion, should it carry: Fixes: bee25f97ad24 ("powerpc: Enable GENERIC_ENTRY feature") On the claim that other GENERIC_ENTRY architectures "do not hit this because their CI toolchains are always new enough": x86, s390, riscv and loongarch with GCC 8 to 12 build exactly the same configuration, CONFIG_KASAN=y without CONFIG_CC_HAS_KASAN_MEMINTRINSIC_PREFIX, and run with mem*() unchecked. scripts/Makefile.kasan documents that as the intended behaviour: # Instrument memcpy/memset/memmove calls by using instrumented __asan_mem*() # instead. With compilers that don't support this option, compiler-inserted # memintrinsics won't be checked by KASAN on GENERIC_ENTRY architectures. and mm/kasan/kasan_test_c.c skips the affected tests with "Test requires checked mem*()". So the situation the message describes is the accepted upstream state for old toolchains, not something specific to powerpc. > diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig > index 2580e27e4328..b27ed9739eea 100644 > --- a/arch/powerpc/Kconfig > +++ b/arch/powerpc/Kconfig > @@ -7,6 +7,10 @@ config CC_HAS_ELFV2 > config CC_HAS_PREFIXED > def_bool PPC64 && $(cc-option, -mcpu=power10 -mprefixed) > > +config PPC_CC_HAS_KASAN_MEMINTRINSIC_PREFIX > + def_bool (CC_IS_CLANG && $(cc-option,-fsanitize=kernel-address -mllvm > -asan-kernel-mem-intrinsic-prefix=1)) || \ > + (CC_IS_GCC && $(cc-option,-fsanitize=kernel-address --param > asan-kernel-mem-intrinsic-prefix=1)) > + > config CC_HAS_PCREL > # Clang has a bug (https://github.com/llvm/llvm-project/issues/62372) > # where pcrel code is not generated if -msoft-float, -mno-altivec, or [ ... ] > @@ -220,9 +224,9 @@ config PPC > select HAVE_ARCH_HUGE_VMAP if PPC_RADIX_MMU || PPC_8xx > select HAVE_ARCH_JUMP_LABEL > select HAVE_ARCH_JUMP_LABEL_RELATIVE > - select HAVE_ARCH_KASAN if PPC32 && PAGE_SHIFT <= 14 > - select HAVE_ARCH_KASAN if PPC_RADIX_MMU > - select HAVE_ARCH_KASAN if PPC_BOOK3E_64 > + select HAVE_ARCH_KASAN if PPC32 && PAGE_SHIFT <= 14 && > PPC_CC_HAS_KASAN_MEMINTRINSIC_PREFIX > + select HAVE_ARCH_KASAN if PPC_RADIX_MMU && > PPC_CC_HAS_KASAN_MEMINTRINSIC_PREFIX > + select HAVE_ARCH_KASAN if PPC_BOOK3E_64 && > PPC_CC_HAS_KASAN_MEMINTRINSIC_PREFIX > select HAVE_ARCH_KASAN_VMALLOC if HAVE_ARCH_KASAN > select HAVE_ARCH_KCSAN > select HAVE_ARCH_KFENCE if ARCH_SUPPORTS_DEBUG_PAGEALLOC This drops KASAN from powerpc entirely for GCC 8 through 12, which are inside the supported range in Documentation/process/changes.rst (GNU C 8.1 minimum). An existing .config with CONFIG_KASAN=y silently loses it on olddefconfig once HAVE_ARCH_KASAN is no longer selected. Given that the actual breakage is the broken memcpy/memmove entry points, is it necessary to go this far? Keeping one _GLOBAL_TOC() prologue for __memcpy and making memcpy a plain alias of it (a second label plus a matching .localentry, or a global entry that branches to __memcpy) would restore the pre-GENERIC_ENTRY behaviour, with mem*() unchecked on old compilers exactly like the other GENERIC_ENTRY architectures. That also keeps a Fixes-tagged backport candidate from removing a feature on stable kernels. > diff --git a/arch/powerpc/include/asm/kasan.h > b/arch/powerpc/include/asm/kasan.h > index a690e7da53c2..d62756b87ba4 100644 > --- a/arch/powerpc/include/asm/kasan.h > +++ b/arch/powerpc/include/asm/kasan.h > @@ -2,20 +2,9 @@ > #ifndef __ASM_KASAN_H > #define __ASM_KASAN_H > > -#if defined(CONFIG_KASAN) && > !defined(CONFIG_CC_HAS_KASAN_MEMINTRINSIC_PREFIX) > -#define _GLOBAL_KASAN(fn) \ > - _GLOBAL(fn); \ > - _GLOBAL(__##fn) > -#define _GLOBAL_TOC_KASAN(fn) \ > - _GLOBAL_TOC(fn); \ > - _GLOBAL_TOC(__##fn) > -#define EXPORT_SYMBOL_KASAN(fn) \ > - EXPORT_SYMBOL(__##fn) > -#else /* CONFIG_KASAN && !CONFIG_CC_HAS_KASAN_MEMINTRINSIC_PREFIX */ > #define _GLOBAL_KASAN(fn) _GLOBAL(fn) > #define _GLOBAL_TOC_KASAN(fn) _GLOBAL_TOC(fn) > #define EXPORT_SYMBOL_KASAN(fn) > -#endif /* CONFIG_KASAN && !CONFIG_CC_HAS_KASAN_MEMINTRINSIC_PREFIX */ > > #ifndef __ASSEMBLER__ > this isn't a bug, but after this change _GLOBAL_KASAN(), _GLOBAL_TOC_KASAN() and EXPORT_SYMBOL_KASAN() are unconditional identity macros with a single empty one. Should the five users in mem_64.S, memcpy_64.S and copy_32.S switch to _GLOBAL()/_GLOBAL_TOC() and the macros go away? Related leftover: arch/powerpc/kernel/prom_init_check.sh still has has_renamed_memintrinsics() { grep -q "^CONFIG_KASAN=y$" "${KCONFIG_CONFIG}" && \ ! grep -q "^CONFIG_CC_HAS_KASAN_MEMINTRINSIC_PREFIX=y" "${KCONFIG_CONFIG}" } if has_renamed_memintrinsics then MEM_FUNCS="__memcpy __memset" which can no longer be true on powerpc after this commit. Should that branch be removed in the same cleanup? > diff --git a/arch/powerpc/include/asm/string.h > b/arch/powerpc/include/asm/string.h > index 1981bd4036b5..72b5c93a2b84 100644 > --- a/arch/powerpc/include/asm/string.h > +++ b/arch/powerpc/include/asm/string.h > @@ -29,29 +29,10 @@ extern void * memchr(const void *,int,__kernel_size_t); > void memcpy_flushcache(void *dest, const void *src, size_t size); > > #ifdef CONFIG_KASAN > -/* __mem variants are used by KASAN to implement instrumented > meminstrinsics. */ > -#ifdef CONFIG_CC_HAS_KASAN_MEMINTRINSIC_PREFIX > +/* Used by mm/kasan/shadow.c as raw backends to bypass KASAN checking. */ > #define __memset memset this isn't a bug, but the new comment names only mm/kasan/shadow.c. The same __memset()/__memcpy() names are used by mm/kasan/generic.c as well (DEFINE_ASAN_SET_SHADOW() and release_alloc_meta()), so would "used by mm/kasan as raw backends" be more accurate? --- diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index b27ed9739eea..2580e27e4328 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -7,10 +7,6 @@ config CC_HAS_ELFV2 config CC_HAS_PREFIXED def_bool PPC64 && $(cc-option, -mcpu=power10 -mprefixed) -config PPC_CC_HAS_KASAN_MEMINTRINSIC_PREFIX - def_bool (CC_IS_CLANG && $(cc-option,-fsanitize=kernel-address -mllvm -asan-kernel-mem-intrinsic-prefix=1)) || \ - (CC_IS_GCC && $(cc-option,-fsanitize=kernel-address --param asan-kernel-mem-intrinsic-prefix=1)) - config CC_HAS_PCREL # Clang has a bug (https://github.com/llvm/llvm-project/issues/62372) # where pcrel code is not generated if -msoft-float, -mno-altivec, or @@ -224,9 +220,9 @@ config PPC select HAVE_ARCH_HUGE_VMAP if PPC_RADIX_MMU || PPC_8xx select HAVE_ARCH_JUMP_LABEL select HAVE_ARCH_JUMP_LABEL_RELATIVE - select HAVE_ARCH_KASAN if PPC32 && PAGE_SHIFT <= 14 && PPC_CC_HAS_KASAN_MEMINTRINSIC_PREFIX - select HAVE_ARCH_KASAN if PPC_RADIX_MMU && PPC_CC_HAS_KASAN_MEMINTRINSIC_PREFIX - select HAVE_ARCH_KASAN if PPC_BOOK3E_64 && PPC_CC_HAS_KASAN_MEMINTRINSIC_PREFIX + select HAVE_ARCH_KASAN if PPC32 && PAGE_SHIFT <= 14 + select HAVE_ARCH_KASAN if PPC_RADIX_MMU + select HAVE_ARCH_KASAN if PPC_BOOK3E_64 select HAVE_ARCH_KASAN_VMALLOC if HAVE_ARCH_KASAN select HAVE_ARCH_KCSAN select HAVE_ARCH_KFENCE if ARCH_SUPPORTS_DEBUG_PAGEALLOC diff --git a/arch/powerpc/include/asm/kasan.h b/arch/powerpc/include/asm/kasan.h index d62756b87ba4..599a9e02af02 100644 --- a/arch/powerpc/include/asm/kasan.h +++ b/arch/powerpc/include/asm/kasan.h @@ -2,10 +2,6 @@ #ifndef __ASM_KASAN_H #define __ASM_KASAN_H -#define _GLOBAL_KASAN(fn) _GLOBAL(fn) -#define _GLOBAL_TOC_KASAN(fn) _GLOBAL_TOC(fn) -#define EXPORT_SYMBOL_KASAN(fn) - #ifndef __ASSEMBLER__ #include <asm/page.h> diff --git a/arch/powerpc/kernel/prom_init_check.sh b/arch/powerpc/kernel/prom_init_check.sh index 3090b97258ae..3155cc722e48 100644 --- a/arch/powerpc/kernel/prom_init_check.sh +++ b/arch/powerpc/kernel/prom_init_check.sh @@ -13,21 +13,8 @@ # If you really need to reference something from prom_init.o add # it to the list below: -has_renamed_memintrinsics() -{ - grep -q "^CONFIG_KASAN=y$" "${KCONFIG_CONFIG}" && \ - ! grep -q "^CONFIG_CC_HAS_KASAN_MEMINTRINSIC_PREFIX=y" "${KCONFIG_CONFIG}" -} - -if has_renamed_memintrinsics -then - MEM_FUNCS="__memcpy __memset" -else - MEM_FUNCS="memcpy memset" -fi - WHITELIST="add_reloc_offset __bss_start __bss_stop copy_and_flush -_end enter_prom $MEM_FUNCS reloc_offset __secondary_hold +_end enter_prom memcpy memset reloc_offset __secondary_hold __secondary_hold_acknowledge __secondary_hold_spinloop __start logo_linux_clut224 btext_prepare_BAT reloc_got2 kernstart_addr memstart_addr linux_banner _stext diff --git a/arch/powerpc/lib/copy_32.S b/arch/powerpc/lib/copy_32.S index 933b685e7ab6..97eb9ca0cc23 100644 --- a/arch/powerpc/lib/copy_32.S +++ b/arch/powerpc/lib/copy_32.S @@ -10,7 +10,6 @@ #include <asm/errno.h> #include <asm/ppc_asm.h> #include <asm/code-patching-asm.h> -#include <asm/kasan.h> #define COPY_16_BYTES \ lwz r7,4(r4); \ @@ -87,7 +86,7 @@ EXPORT_SYMBOL(memset16) * We therefore skip the optimised bloc that uses dcbz. This jump is * replaced by a nop once cache is active. This is done in machine_init() */ -_GLOBAL_KASAN(memset) +_GLOBAL(memset) cmplwi 0,r5,4 blt 7f @@ -147,7 +146,6 @@ _GLOBAL_KASAN(memset) bdnz 9b blr EXPORT_SYMBOL(memset) -EXPORT_SYMBOL_KASAN(memset) /* * This version uses dcbz on the complete cache lines in the @@ -160,12 +158,12 @@ EXPORT_SYMBOL_KASAN(memset) * We therefore jump to generic_memcpy which doesn't use dcbz. This jump is * replaced by a nop once cache is active. This is done in machine_init() */ -_GLOBAL_KASAN(memmove) +_GLOBAL(memmove) cmplw 0,r3,r4 bgt backwards_memcpy /* fall through */ -_GLOBAL_KASAN(memcpy) +_GLOBAL(memcpy) 1: b generic_memcpy patch_site 1b, patch__memcpy_nocache @@ -241,8 +239,6 @@ _GLOBAL_KASAN(memcpy) 65: blr EXPORT_SYMBOL(memcpy) EXPORT_SYMBOL(memmove) -EXPORT_SYMBOL_KASAN(memcpy) -EXPORT_SYMBOL_KASAN(memmove) generic_memcpy: srwi. r7,r5,3 diff --git a/arch/powerpc/lib/mem_64.S b/arch/powerpc/lib/mem_64.S index 6fd06cd20faa..40eaedd31486 100644 --- a/arch/powerpc/lib/mem_64.S +++ b/arch/powerpc/lib/mem_64.S @@ -8,7 +8,6 @@ #include <asm/processor.h> #include <asm/errno.h> #include <asm/ppc_asm.h> -#include <asm/kasan.h> #ifndef CONFIG_KASAN _GLOBAL(__memset16) @@ -29,7 +28,7 @@ EXPORT_SYMBOL(__memset32) EXPORT_SYMBOL(__memset64) #endif -_GLOBAL_KASAN(memset) +_GLOBAL(memset) neg r0,r3 rlwimi r4,r4,8,16,23 andi. r0,r0,7 /* # bytes to be 8-byte aligned */ @@ -95,9 +94,8 @@ _GLOBAL_KASAN(memset) stb r4,0(r6) blr EXPORT_SYMBOL(memset) -EXPORT_SYMBOL_KASAN(memset) -_GLOBAL_TOC_KASAN(memmove) +_GLOBAL_TOC(memmove) cmplw 0,r3,r4 bgt backwards_memcpy b memcpy @@ -139,4 +137,3 @@ _GLOBAL(backwards_memcpy) mtctr r7 b 1b EXPORT_SYMBOL(memmove) -EXPORT_SYMBOL_KASAN(memmove) diff --git a/arch/powerpc/lib/memcpy_64.S b/arch/powerpc/lib/memcpy_64.S index b5a67e20143f..0cedd455231a 100644 --- a/arch/powerpc/lib/memcpy_64.S +++ b/arch/powerpc/lib/memcpy_64.S @@ -7,7 +7,6 @@ #include <asm/ppc_asm.h> #include <asm/asm-compat.h> #include <asm/feature-fixups.h> -#include <asm/kasan.h> #ifndef SELFTEST_CASE /* For big-endian, 0 == most CPUs, 1 == POWER6, 2 == Cell */ @@ -15,7 +14,7 @@ #endif .align 7 -_GLOBAL_TOC_KASAN(memcpy) +_GLOBAL_TOC(memcpy) BEGIN_FTR_SECTION #ifdef __LITTLE_ENDIAN__ cmpdi cr7,r5,0 @@ -227,4 +226,3 @@ END_FTR_SECTION_IFCLR(CPU_FTR_UNALIGNED_LD_STD) blr #endif EXPORT_SYMBOL(memcpy) -EXPORT_SYMBOL_KASAN(memcpy)

