Skip to content

Commit efb07ac

Browse files
jlintonarmwilldeacon
authored andcommitted
arm64: probes: Add GCS support to bl/blr/ret
The arm64 probe simulation doesn't currently have logic in place to deal with GCS and this results in core dumps if probes are inserted at control flow locations. Fix-up bl, blr and ret to manipulate the shadow stack as needed. While we manipulate and validate the shadow stack correctly, the hardware provides additional security by only allowing GCS operations against pages which are marked to support GCS. For writing there is gcssttr() which enforces this, but there isn't an equivalent for reading. This means that uprobe users should be aware that probing on control flow instructions which require reading the shadow stack (ex: ret) offers lower security guarantees than what is achieved without the uprobe active. Signed-off-by: Jeremy Linton <jeremy.linton@arm.com> Reviewed-by: Catalin Marinas <catalin.marinas@arm.com> Signed-off-by: Will Deacon <will@kernel.org>
1 parent 9cd2a7f commit efb07ac

1 file changed

Lines changed: 35 additions & 9 deletions

File tree

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

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <asm/traps.h>
1414

1515
#include "simulate-insn.h"
16+
#include "asm/gcs.h"
1617

1718
#define bbl_displacement(insn) \
1819
sign_extend32(((insn) & 0x3ffffff) << 2, 27)
@@ -49,6 +50,21 @@ static inline u32 get_w_reg(struct pt_regs *regs, int reg)
4950
return lower_32_bits(pt_regs_read_reg(regs, reg));
5051
}
5152

53+
static inline int update_lr(struct pt_regs *regs, long addr)
54+
{
55+
int err = 0;
56+
57+
if (user_mode(regs) && task_gcs_el0_enabled(current)) {
58+
push_user_gcs(addr, &err);
59+
if (err) {
60+
force_sig(SIGSEGV);
61+
return err;
62+
}
63+
}
64+
procedure_link_pointer_set(regs, addr);
65+
return err;
66+
}
67+
5268
static bool __kprobes check_cbz(u32 opcode, struct pt_regs *regs)
5369
{
5470
int xn = opcode & 0x1f;
@@ -107,9 +123,9 @@ simulate_b_bl(u32 opcode, long addr, struct pt_regs *regs)
107123
{
108124
int disp = bbl_displacement(opcode);
109125

110-
/* Link register is x30 */
111126
if (opcode & (1 << 31))
112-
set_x_reg(regs, 30, addr + 4);
127+
if (update_lr(regs, addr + 4))
128+
return;
113129

114130
instruction_pointer_set(regs, addr + disp);
115131
}
@@ -129,21 +145,31 @@ void __kprobes
129145
simulate_br_blr(u32 opcode, long addr, struct pt_regs *regs)
130146
{
131147
int xn = (opcode >> 5) & 0x1f;
148+
int b_target = get_x_reg(regs, xn);
132149

133-
/* update pc first in case we're doing a "blr lr" */
134-
instruction_pointer_set(regs, get_x_reg(regs, xn));
135-
136-
/* Link register is x30 */
137150
if (((opcode >> 21) & 0x3) == 1)
138-
set_x_reg(regs, 30, addr + 4);
151+
if (update_lr(regs, addr + 4))
152+
return;
153+
154+
instruction_pointer_set(regs, b_target);
139155
}
140156

141157
void __kprobes
142158
simulate_ret(u32 opcode, long addr, struct pt_regs *regs)
143159
{
160+
u64 ret_addr;
161+
int err = 0;
144162
int xn = (opcode >> 5) & 0x1f;
145-
146-
instruction_pointer_set(regs, get_x_reg(regs, xn));
163+
unsigned long r_target = get_x_reg(regs, xn);
164+
165+
if (user_mode(regs) && task_gcs_el0_enabled(current)) {
166+
ret_addr = pop_user_gcs(&err);
167+
if (err || ret_addr != r_target) {
168+
force_sig(SIGSEGV);
169+
return;
170+
}
171+
}
172+
instruction_pointer_set(regs, r_target);
147173
}
148174

149175
void __kprobes

0 commit comments

Comments
 (0)