Skip to content

Commit 9772b7f

Browse files
committed
Merge branch 'for-next/stacktrace' into for-next/core
* for-next/stacktrace: arm64: move PAC masks to <asm/pointer_auth.h> arm64: use XPACLRI to strip PAC arm64: avoid redundant PAC stripping in __builtin_return_address() arm64: stacktrace: always inline core stacktrace functions arm64: stacktrace: move dump functions to end of file arm64: stacktrace: recover return address for first entry
2 parents 9651f00 + de1702f commit 9772b7f

File tree

7 files changed

+126
-86
lines changed

7 files changed

+126
-86
lines changed

arch/arm64/Kconfig

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,20 @@ config ARCH_PROC_KCORE_TEXT
366366
config BROKEN_GAS_INST
367367
def_bool !$(as-instr,1:\n.inst 0\n.rept . - 1b\n\nnop\n.endr\n)
368368

369+
config BUILTIN_RETURN_ADDRESS_STRIPS_PAC
370+
bool
371+
# Clang's __builtin_return_adddress() strips the PAC since 12.0.0
372+
# https://reviews.llvm.org/D75044
373+
default y if CC_IS_CLANG && (CLANG_VERSION >= 120000)
374+
# GCC's __builtin_return_address() strips the PAC since 11.1.0,
375+
# and this was backported to 10.2.0, 9.4.0, 8.5.0, but not earlier
376+
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94891
377+
default y if CC_IS_GCC && (GCC_VERSION >= 110100)
378+
default y if CC_IS_GCC && (GCC_VERSION >= 100200) && (GCC_VERSION < 110000)
379+
default y if CC_IS_GCC && (GCC_VERSION >= 90400) && (GCC_VERSION < 100000)
380+
default y if CC_IS_GCC && (GCC_VERSION >= 80500) && (GCC_VERSION < 90000)
381+
default n
382+
369383
config KASAN_SHADOW_OFFSET
370384
hex
371385
depends on KASAN_GENERIC || KASAN_SW_TAGS

arch/arm64/include/asm/compiler.h

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,33 @@
88
#define ARM64_ASM_PREAMBLE
99
#endif
1010

11-
/*
12-
* The EL0/EL1 pointer bits used by a pointer authentication code.
13-
* This is dependent on TBI0/TBI1 being enabled, or bits 63:56 would also apply.
14-
*/
15-
#define ptrauth_user_pac_mask() GENMASK_ULL(54, vabits_actual)
16-
#define ptrauth_kernel_pac_mask() GENMASK_ULL(63, vabits_actual)
11+
#define xpaclri(ptr) \
12+
({ \
13+
register unsigned long __xpaclri_ptr asm("x30") = (ptr); \
14+
\
15+
asm( \
16+
ARM64_ASM_PREAMBLE \
17+
" hint #7\n" \
18+
: "+r" (__xpaclri_ptr)); \
19+
\
20+
__xpaclri_ptr; \
21+
})
1722

18-
/* Valid for EL0 TTBR0 and EL1 TTBR1 instruction pointers */
19-
#define ptrauth_clear_pac(ptr) \
20-
((ptr & BIT_ULL(55)) ? (ptr | ptrauth_kernel_pac_mask()) : \
21-
(ptr & ~ptrauth_user_pac_mask()))
23+
#ifdef CONFIG_ARM64_PTR_AUTH_KERNEL
24+
#define ptrauth_strip_kernel_insn_pac(ptr) xpaclri(ptr)
25+
#else
26+
#define ptrauth_strip_kernel_insn_pac(ptr) (ptr)
27+
#endif
28+
29+
#ifdef CONFIG_ARM64_PTR_AUTH
30+
#define ptrauth_strip_user_insn_pac(ptr) xpaclri(ptr)
31+
#else
32+
#define ptrauth_strip_user_insn_pac(ptr) (ptr)
33+
#endif
2234

35+
#if !defined(CONFIG_BUILTIN_RETURN_ADDRESS_STRIPS_PAC)
2336
#define __builtin_return_address(val) \
24-
(void *)(ptrauth_clear_pac((unsigned long)__builtin_return_address(val)))
37+
(void *)(ptrauth_strip_kernel_insn_pac((unsigned long)__builtin_return_address(val)))
38+
#endif
2539

2640
#endif /* __ASM_COMPILER_H */

arch/arm64/include/asm/pointer_auth.h

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,13 @@
1010
#include <asm/memory.h>
1111
#include <asm/sysreg.h>
1212

13+
/*
14+
* The EL0/EL1 pointer bits used by a pointer authentication code.
15+
* This is dependent on TBI0/TBI1 being enabled, or bits 63:56 would also apply.
16+
*/
17+
#define ptrauth_user_pac_mask() GENMASK_ULL(54, vabits_actual)
18+
#define ptrauth_kernel_pac_mask() GENMASK_ULL(63, vabits_actual)
19+
1320
#define PR_PAC_ENABLED_KEYS_MASK \
1421
(PR_PAC_APIAKEY | PR_PAC_APIBKEY | PR_PAC_APDAKEY | PR_PAC_APDBKEY)
1522

@@ -97,11 +104,6 @@ extern int ptrauth_set_enabled_keys(struct task_struct *tsk, unsigned long keys,
97104
unsigned long enabled);
98105
extern int ptrauth_get_enabled_keys(struct task_struct *tsk);
99106

100-
static inline unsigned long ptrauth_strip_insn_pac(unsigned long ptr)
101-
{
102-
return ptrauth_clear_pac(ptr);
103-
}
104-
105107
static __always_inline void ptrauth_enable(void)
106108
{
107109
if (!system_supports_address_auth())
@@ -133,7 +135,6 @@ static __always_inline void ptrauth_enable(void)
133135
#define ptrauth_prctl_reset_keys(tsk, arg) (-EINVAL)
134136
#define ptrauth_set_enabled_keys(tsk, keys, enabled) (-EINVAL)
135137
#define ptrauth_get_enabled_keys(tsk) (-EINVAL)
136-
#define ptrauth_strip_insn_pac(lr) (lr)
137138
#define ptrauth_suspend_exit()
138139
#define ptrauth_thread_init_user()
139140
#define ptrauth_thread_switch_user(tsk)

arch/arm64/kernel/crash_core.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <asm/cpufeature.h>
99
#include <asm/memory.h>
1010
#include <asm/pgtable-hwdef.h>
11+
#include <asm/pointer_auth.h>
1112

1213
static inline u64 get_tcr_el1_t1sz(void);
1314

arch/arm64/kernel/perf_callchain.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ user_backtrace(struct frame_tail __user *tail,
3838
if (err)
3939
return NULL;
4040

41-
lr = ptrauth_strip_insn_pac(buftail.lr);
41+
lr = ptrauth_strip_user_insn_pac(buftail.lr);
4242

4343
perf_callchain_store(entry, lr);
4444

arch/arm64/kernel/process.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ void __show_regs(struct pt_regs *regs)
217217

218218
if (!user_mode(regs)) {
219219
printk("pc : %pS\n", (void *)regs->pc);
220-
printk("lr : %pS\n", (void *)ptrauth_strip_insn_pac(lr));
220+
printk("lr : %pS\n", (void *)ptrauth_strip_kernel_insn_pac(lr));
221221
} else {
222222
printk("pc : %016llx\n", regs->pc);
223223
printk("lr : %016llx\n", lr);

arch/arm64/kernel/stacktrace.c

Lines changed: 77 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,9 @@
2525
*
2626
* The regs must be on a stack currently owned by the calling task.
2727
*/
28-
static __always_inline void unwind_init_from_regs(struct unwind_state *state,
29-
struct pt_regs *regs)
28+
static __always_inline void
29+
unwind_init_from_regs(struct unwind_state *state,
30+
struct pt_regs *regs)
3031
{
3132
unwind_init_common(state, current);
3233

@@ -42,7 +43,8 @@ static __always_inline void unwind_init_from_regs(struct unwind_state *state,
4243
*
4344
* The function which invokes this must be noinline.
4445
*/
45-
static __always_inline void unwind_init_from_caller(struct unwind_state *state)
46+
static __always_inline void
47+
unwind_init_from_caller(struct unwind_state *state)
4648
{
4749
unwind_init_common(state, current);
4850

@@ -60,23 +62,51 @@ static __always_inline void unwind_init_from_caller(struct unwind_state *state)
6062
* duration of the unwind, or the unwind will be bogus. It is never valid to
6163
* call this for the current task.
6264
*/
63-
static __always_inline void unwind_init_from_task(struct unwind_state *state,
64-
struct task_struct *task)
65+
static __always_inline void
66+
unwind_init_from_task(struct unwind_state *state,
67+
struct task_struct *task)
6568
{
6669
unwind_init_common(state, task);
6770

6871
state->fp = thread_saved_fp(task);
6972
state->pc = thread_saved_pc(task);
7073
}
7174

75+
static __always_inline int
76+
unwind_recover_return_address(struct unwind_state *state)
77+
{
78+
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
79+
if (state->task->ret_stack &&
80+
(state->pc == (unsigned long)return_to_handler)) {
81+
unsigned long orig_pc;
82+
orig_pc = ftrace_graph_ret_addr(state->task, NULL, state->pc,
83+
(void *)state->fp);
84+
if (WARN_ON_ONCE(state->pc == orig_pc))
85+
return -EINVAL;
86+
state->pc = orig_pc;
87+
}
88+
#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
89+
90+
#ifdef CONFIG_KRETPROBES
91+
if (is_kretprobe_trampoline(state->pc)) {
92+
state->pc = kretprobe_find_ret_addr(state->task,
93+
(void *)state->fp,
94+
&state->kr_cur);
95+
}
96+
#endif /* CONFIG_KRETPROBES */
97+
98+
return 0;
99+
}
100+
72101
/*
73102
* Unwind from one frame record (A) to the next frame record (B).
74103
*
75104
* We terminate early if the location of B indicates a malformed chain of frame
76105
* records (e.g. a cycle), determined based on the location and fp value of A
77106
* and the location (but not the fp value) of B.
78107
*/
79-
static int notrace unwind_next(struct unwind_state *state)
108+
static __always_inline int
109+
unwind_next(struct unwind_state *state)
80110
{
81111
struct task_struct *tsk = state->task;
82112
unsigned long fp = state->fp;
@@ -90,37 +120,18 @@ static int notrace unwind_next(struct unwind_state *state)
90120
if (err)
91121
return err;
92122

93-
state->pc = ptrauth_strip_insn_pac(state->pc);
94-
95-
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
96-
if (tsk->ret_stack &&
97-
(state->pc == (unsigned long)return_to_handler)) {
98-
unsigned long orig_pc;
99-
/*
100-
* This is a case where function graph tracer has
101-
* modified a return address (LR) in a stack frame
102-
* to hook a function return.
103-
* So replace it to an original value.
104-
*/
105-
orig_pc = ftrace_graph_ret_addr(tsk, NULL, state->pc,
106-
(void *)state->fp);
107-
if (WARN_ON_ONCE(state->pc == orig_pc))
108-
return -EINVAL;
109-
state->pc = orig_pc;
110-
}
111-
#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
112-
#ifdef CONFIG_KRETPROBES
113-
if (is_kretprobe_trampoline(state->pc))
114-
state->pc = kretprobe_find_ret_addr(tsk, (void *)state->fp, &state->kr_cur);
115-
#endif
123+
state->pc = ptrauth_strip_kernel_insn_pac(state->pc);
116124

117-
return 0;
125+
return unwind_recover_return_address(state);
118126
}
119-
NOKPROBE_SYMBOL(unwind_next);
120127

121-
static void notrace unwind(struct unwind_state *state,
122-
stack_trace_consume_fn consume_entry, void *cookie)
128+
static __always_inline void
129+
unwind(struct unwind_state *state, stack_trace_consume_fn consume_entry,
130+
void *cookie)
123131
{
132+
if (unwind_recover_return_address(state))
133+
return;
134+
124135
while (1) {
125136
int ret;
126137

@@ -131,40 +142,6 @@ static void notrace unwind(struct unwind_state *state,
131142
break;
132143
}
133144
}
134-
NOKPROBE_SYMBOL(unwind);
135-
136-
static bool dump_backtrace_entry(void *arg, unsigned long where)
137-
{
138-
char *loglvl = arg;
139-
printk("%s %pSb\n", loglvl, (void *)where);
140-
return true;
141-
}
142-
143-
void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk,
144-
const char *loglvl)
145-
{
146-
pr_debug("%s(regs = %p tsk = %p)\n", __func__, regs, tsk);
147-
148-
if (regs && user_mode(regs))
149-
return;
150-
151-
if (!tsk)
152-
tsk = current;
153-
154-
if (!try_get_task_stack(tsk))
155-
return;
156-
157-
printk("%sCall trace:\n", loglvl);
158-
arch_stack_walk(dump_backtrace_entry, (void *)loglvl, tsk, regs);
159-
160-
put_task_stack(tsk);
161-
}
162-
163-
void show_stack(struct task_struct *tsk, unsigned long *sp, const char *loglvl)
164-
{
165-
dump_backtrace(NULL, tsk, loglvl);
166-
barrier();
167-
}
168145

169146
/*
170147
* Per-cpu stacks are only accessible when unwinding the current task in a
@@ -230,3 +207,36 @@ noinline noinstr void arch_stack_walk(stack_trace_consume_fn consume_entry,
230207

231208
unwind(&state, consume_entry, cookie);
232209
}
210+
211+
static bool dump_backtrace_entry(void *arg, unsigned long where)
212+
{
213+
char *loglvl = arg;
214+
printk("%s %pSb\n", loglvl, (void *)where);
215+
return true;
216+
}
217+
218+
void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk,
219+
const char *loglvl)
220+
{
221+
pr_debug("%s(regs = %p tsk = %p)\n", __func__, regs, tsk);
222+
223+
if (regs && user_mode(regs))
224+
return;
225+
226+
if (!tsk)
227+
tsk = current;
228+
229+
if (!try_get_task_stack(tsk))
230+
return;
231+
232+
printk("%sCall trace:\n", loglvl);
233+
arch_stack_walk(dump_backtrace_entry, (void *)loglvl, tsk, regs);
234+
235+
put_task_stack(tsk);
236+
}
237+
238+
void show_stack(struct task_struct *tsk, unsigned long *sp, const char *loglvl)
239+
{
240+
dump_backtrace(NULL, tsk, loglvl);
241+
barrier();
242+
}

0 commit comments

Comments
 (0)