Patch "arm64: alternatives: mark patch_alternative() as `noinstr`" has been added to the 4.19-stable tree

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This is a note to let you know that I've just added the patch titled

    arm64: alternatives: mark patch_alternative() as `noinstr`

to the 4.19-stable tree which can be found at:
    http://www.kernel.org/git/?p=linux/kernel/git/stable/stable-queue.git;a=summary

The filename of the patch is:
     arm64-alternatives-mark-patch_alternative-as-noinstr.patch
and it can be found in the queue-4.19 subdirectory.

If you, or anyone else, feels it should not be added to the stable tree,
please let <stable@xxxxxxxxxxxxxxx> know about it.



commit 5f696dace46db402ec5b88bf6fd1a70fbdbfe9c0
Author: Joey Gouly <joey.gouly@xxxxxxx>
Date:   Tue Apr 5 11:47:33 2022 +0100

    arm64: alternatives: mark patch_alternative() as `noinstr`
    
    [ Upstream commit a2c0b0fbe01419f8f5d1c0b9c581631f34ffce8b ]
    
    The alternatives code must be `noinstr` such that it does not patch itself,
    as the cache invalidation is only performed after all the alternatives have
    been applied.
    
    Mark patch_alternative() as `noinstr`. Mark branch_insn_requires_update()
    and get_alt_insn() with `__always_inline` since they are both only called
    through patch_alternative().
    
    Booting a kernel in QEMU TCG with KCSAN=y and ARM64_USE_LSE_ATOMICS=y caused
    a boot hang:
    [    0.241121] CPU: All CPU(s) started at EL2
    
    The alternatives code was patching the atomics in __tsan_read4() from LL/SC
    atomics to LSE atomics.
    
    The following fragment is using LL/SC atomics in the .text section:
      | <__tsan_unaligned_read4+304>:     ldxr    x6, [x2]
      | <__tsan_unaligned_read4+308>:     add     x6, x6, x5
      | <__tsan_unaligned_read4+312>:     stxr    w7, x6, [x2]
      | <__tsan_unaligned_read4+316>:     cbnz    w7, <__tsan_unaligned_read4+304>
    
    This LL/SC atomic sequence was to be replaced with LSE atomics. However since
    the alternatives code was instrumentable, __tsan_read4() was being called after
    only the first instruction was replaced, which led to the following code in memory:
      | <__tsan_unaligned_read4+304>:     ldadd   x5, x6, [x2]
      | <__tsan_unaligned_read4+308>:     add     x6, x6, x5
      | <__tsan_unaligned_read4+312>:     stxr    w7, x6, [x2]
      | <__tsan_unaligned_read4+316>:     cbnz    w7, <__tsan_unaligned_read4+304>
    
    This caused an infinite loop as the `stxr` instruction never completed successfully,
    so `w7` was always 0.
    
    Signed-off-by: Joey Gouly <joey.gouly@xxxxxxx>
    Cc: Mark Rutland <mark.rutland@xxxxxxx>
    Cc: Catalin Marinas <catalin.marinas@xxxxxxx>
    Cc: Will Deacon <will@xxxxxxxxxx>
    Link: https://lore.kernel.org/r/20220405104733.11476-1-joey.gouly@xxxxxxx
    Signed-off-by: Will Deacon <will@xxxxxxxxxx>
    Signed-off-by: Sasha Levin <sashal@xxxxxxxxxx>

diff --git a/arch/arm64/kernel/alternative.c b/arch/arm64/kernel/alternative.c
index 0d345622bbba..3747c8d87bdb 100644
--- a/arch/arm64/kernel/alternative.c
+++ b/arch/arm64/kernel/alternative.c
@@ -42,7 +42,7 @@ struct alt_region {
 /*
  * Check if the target PC is within an alternative block.
  */
-static bool branch_insn_requires_update(struct alt_instr *alt, unsigned long pc)
+static __always_inline bool branch_insn_requires_update(struct alt_instr *alt, unsigned long pc)
 {
 	unsigned long replptr = (unsigned long)ALT_REPL_PTR(alt);
 	return !(pc >= replptr && pc <= (replptr + alt->alt_len));
@@ -50,7 +50,7 @@ static bool branch_insn_requires_update(struct alt_instr *alt, unsigned long pc)
 
 #define align_down(x, a)	((unsigned long)(x) & ~(((unsigned long)(a)) - 1))
 
-static u32 get_alt_insn(struct alt_instr *alt, __le32 *insnptr, __le32 *altinsnptr)
+static __always_inline u32 get_alt_insn(struct alt_instr *alt, __le32 *insnptr, __le32 *altinsnptr)
 {
 	u32 insn;
 
@@ -95,7 +95,7 @@ static u32 get_alt_insn(struct alt_instr *alt, __le32 *insnptr, __le32 *altinsnp
 	return insn;
 }
 
-static void patch_alternative(struct alt_instr *alt,
+static noinstr void patch_alternative(struct alt_instr *alt,
 			      __le32 *origptr, __le32 *updptr, int nr_inst)
 {
 	__le32 *replptr;



[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux