]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
arm64: probes: Fix simulate_ldr*_literal()
authorMark Rutland <mark.rutland@arm.com>
Tue, 8 Oct 2024 15:58:47 +0000 (16:58 +0100)
committerWill Deacon <will@kernel.org>
Wed, 9 Oct 2024 15:56:53 +0000 (16:56 +0100)
The simulate_ldr_literal() code always loads a 64-bit quantity, and when
simulating a 32-bit load into a 'W' register, it discards the most
significant 32 bits. For big-endian kernels this means that the relevant
bits are discarded, and the value returned is the the subsequent 32 bits
in memory (i.e. the value at addr + 4).

Additionally, simulate_ldr_literal() and simulate_ldrsw_literal() use a
plain C load, which the compiler may tear or elide (e.g. if the target
is the zero register). Today this doesn't happen to matter, but it may
matter in future if trampoline code uses a LDR (literal) or LDRSW
(literal).

Update simulate_ldr_literal() and simulate_ldrsw_literal() to use an
appropriately-sized READ_ONCE() to perform the access, which avoids
these problems.

Fixes: 39a67d49ba35 ("arm64: kprobes instruction simulation support")
Cc: stable@vger.kernel.org
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will@kernel.org>
Link: https://lore.kernel.org/r/20241008155851.801546-3-mark.rutland@arm.com
Signed-off-by: Will Deacon <will@kernel.org>
arch/arm64/kernel/probes/simulate-insn.c

index 22d0b32524763e11fe9ce8edee4929c9141504be..b65334ab79d2b0e9190f8349546ebe497c5e5b91 100644 (file)
@@ -171,17 +171,15 @@ simulate_tbz_tbnz(u32 opcode, long addr, struct pt_regs *regs)
 void __kprobes
 simulate_ldr_literal(u32 opcode, long addr, struct pt_regs *regs)
 {
-       u64 *load_addr;
+       unsigned long load_addr;
        int xn = opcode & 0x1f;
-       int disp;
 
-       disp = ldr_displacement(opcode);
-       load_addr = (u64 *) (addr + disp);
+       load_addr = addr + ldr_displacement(opcode);
 
        if (opcode & (1 << 30)) /* x0-x30 */
-               set_x_reg(regs, xn, *load_addr);
+               set_x_reg(regs, xn, READ_ONCE(*(u64 *)load_addr));
        else                    /* w0-w30 */
-               set_w_reg(regs, xn, *load_addr);
+               set_w_reg(regs, xn, READ_ONCE(*(u32 *)load_addr));
 
        instruction_pointer_set(regs, instruction_pointer(regs) + 4);
 }
@@ -189,14 +187,12 @@ simulate_ldr_literal(u32 opcode, long addr, struct pt_regs *regs)
 void __kprobes
 simulate_ldrsw_literal(u32 opcode, long addr, struct pt_regs *regs)
 {
-       s32 *load_addr;
+       unsigned long load_addr;
        int xn = opcode & 0x1f;
-       int disp;
 
-       disp = ldr_displacement(opcode);
-       load_addr = (s32 *) (addr + disp);
+       load_addr = addr + ldr_displacement(opcode);
 
-       set_x_reg(regs, xn, *load_addr);
+       set_x_reg(regs, xn, READ_ONCE(*(s32 *)load_addr));
 
        instruction_pointer_set(regs, instruction_pointer(regs) + 4);
 }