Skip to content

Commit 50f813e

Browse files
mrutland-armwilldeacon
authored andcommitted
arm64: probes: Fix simulate_ldr*_literal()
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: 39a67d4 ("arm64: kprobes instruction simulation support") Cc: [email protected] Signed-off-by: Mark Rutland <[email protected]> Cc: Catalin Marinas <[email protected]> Cc: Will Deacon <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Will Deacon <[email protected]>
1 parent acc450a commit 50f813e

File tree

1 file changed

+7
-11
lines changed

1 file changed

+7
-11
lines changed

arch/arm64/kernel/probes/simulate-insn.c

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -171,32 +171,28 @@ simulate_tbz_tbnz(u32 opcode, long addr, struct pt_regs *regs)
171171
void __kprobes
172172
simulate_ldr_literal(u32 opcode, long addr, struct pt_regs *regs)
173173
{
174-
u64 *load_addr;
174+
unsigned long load_addr;
175175
int xn = opcode & 0x1f;
176-
int disp;
177176

178-
disp = ldr_displacement(opcode);
179-
load_addr = (u64 *) (addr + disp);
177+
load_addr = addr + ldr_displacement(opcode);
180178

181179
if (opcode & (1 << 30)) /* x0-x30 */
182-
set_x_reg(regs, xn, *load_addr);
180+
set_x_reg(regs, xn, READ_ONCE(*(u64 *)load_addr));
183181
else /* w0-w30 */
184-
set_w_reg(regs, xn, *load_addr);
182+
set_w_reg(regs, xn, READ_ONCE(*(u32 *)load_addr));
185183

186184
instruction_pointer_set(regs, instruction_pointer(regs) + 4);
187185
}
188186

189187
void __kprobes
190188
simulate_ldrsw_literal(u32 opcode, long addr, struct pt_regs *regs)
191189
{
192-
s32 *load_addr;
190+
unsigned long load_addr;
193191
int xn = opcode & 0x1f;
194-
int disp;
195192

196-
disp = ldr_displacement(opcode);
197-
load_addr = (s32 *) (addr + disp);
193+
load_addr = addr + ldr_displacement(opcode);
198194

199-
set_x_reg(regs, xn, *load_addr);
195+
set_x_reg(regs, xn, READ_ONCE(*(s32 *)load_addr));
200196

201197
instruction_pointer_set(regs, instruction_pointer(regs) + 4);
202198
}

0 commit comments

Comments
 (0)