Skip to content

Commit 410e471

Browse files
chenqiwuwilldeacon
authored andcommitted
arm64: Add USER_STACKTRACE support
Currently, userstacktrace is unsupported for ftrace and uprobe tracers on arm64. This patch uses the perf_callchain_user() code as blueprint to implement the arch_stack_walk_user() which add userstacktrace support on arm64. Meanwhile, we can use arch_stack_walk_user() to simplify the implementation of perf_callchain_user(). This patch is tested pass with ftrace, uprobe and perf tracers profiling userstacktrace cases. Tested-by: chenqiwu <qiwu.chen@transsion.com> Signed-off-by: chenqiwu <qiwu.chen@transsion.com> Link: https://lore.kernel.org/r/20231219022229.10230-1-qiwu.chen@transsion.com Signed-off-by: Will Deacon <will@kernel.org>
1 parent 582c1ae commit 410e471

File tree

3 files changed

+125
-114
lines changed

3 files changed

+125
-114
lines changed

arch/arm64/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,7 @@ config ARM64
258258
select TRACE_IRQFLAGS_SUPPORT
259259
select TRACE_IRQFLAGS_NMI_SUPPORT
260260
select HAVE_SOFTIRQ_ON_OWN_STACK
261+
select USER_STACKTRACE_SUPPORT
261262
help
262263
ARM 64-bit (AArch64) Linux support.
263264

arch/arm64/kernel/perf_callchain.c

Lines changed: 4 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -10,94 +10,12 @@
1010

1111
#include <asm/pointer_auth.h>
1212

13-
struct frame_tail {
14-
struct frame_tail __user *fp;
15-
unsigned long lr;
16-
} __attribute__((packed));
17-
18-
/*
19-
* Get the return address for a single stackframe and return a pointer to the
20-
* next frame tail.
21-
*/
22-
static struct frame_tail __user *
23-
user_backtrace(struct frame_tail __user *tail,
24-
struct perf_callchain_entry_ctx *entry)
25-
{
26-
struct frame_tail buftail;
27-
unsigned long err;
28-
unsigned long lr;
29-
30-
/* Also check accessibility of one struct frame_tail beyond */
31-
if (!access_ok(tail, sizeof(buftail)))
32-
return NULL;
33-
34-
pagefault_disable();
35-
err = __copy_from_user_inatomic(&buftail, tail, sizeof(buftail));
36-
pagefault_enable();
37-
38-
if (err)
39-
return NULL;
40-
41-
lr = ptrauth_strip_user_insn_pac(buftail.lr);
42-
43-
perf_callchain_store(entry, lr);
44-
45-
/*
46-
* Frame pointers should strictly progress back up the stack
47-
* (towards higher addresses).
48-
*/
49-
if (tail >= buftail.fp)
50-
return NULL;
51-
52-
return buftail.fp;
53-
}
54-
55-
#ifdef CONFIG_COMPAT
56-
/*
57-
* The registers we're interested in are at the end of the variable
58-
* length saved register structure. The fp points at the end of this
59-
* structure so the address of this struct is:
60-
* (struct compat_frame_tail *)(xxx->fp)-1
61-
*
62-
* This code has been adapted from the ARM OProfile support.
63-
*/
64-
struct compat_frame_tail {
65-
compat_uptr_t fp; /* a (struct compat_frame_tail *) in compat mode */
66-
u32 sp;
67-
u32 lr;
68-
} __attribute__((packed));
69-
70-
static struct compat_frame_tail __user *
71-
compat_user_backtrace(struct compat_frame_tail __user *tail,
72-
struct perf_callchain_entry_ctx *entry)
13+
static bool callchain_trace(void *data, unsigned long pc)
7314
{
74-
struct compat_frame_tail buftail;
75-
unsigned long err;
76-
77-
/* Also check accessibility of one struct frame_tail beyond */
78-
if (!access_ok(tail, sizeof(buftail)))
79-
return NULL;
80-
81-
pagefault_disable();
82-
err = __copy_from_user_inatomic(&buftail, tail, sizeof(buftail));
83-
pagefault_enable();
84-
85-
if (err)
86-
return NULL;
87-
88-
perf_callchain_store(entry, buftail.lr);
89-
90-
/*
91-
* Frame pointers should strictly progress back up the stack
92-
* (towards higher addresses).
93-
*/
94-
if (tail + 1 >= (struct compat_frame_tail __user *)
95-
compat_ptr(buftail.fp))
96-
return NULL;
15+
struct perf_callchain_entry_ctx *entry = data;
9716

98-
return (struct compat_frame_tail __user *)compat_ptr(buftail.fp) - 1;
17+
return perf_callchain_store(entry, pc) == 0;
9918
}
100-
#endif /* CONFIG_COMPAT */
10119

10220
void perf_callchain_user(struct perf_callchain_entry_ctx *entry,
10321
struct pt_regs *regs)
@@ -107,35 +25,7 @@ void perf_callchain_user(struct perf_callchain_entry_ctx *entry,
10725
return;
10826
}
10927

110-
perf_callchain_store(entry, regs->pc);
111-
112-
if (!compat_user_mode(regs)) {
113-
/* AARCH64 mode */
114-
struct frame_tail __user *tail;
115-
116-
tail = (struct frame_tail __user *)regs->regs[29];
117-
118-
while (entry->nr < entry->max_stack &&
119-
tail && !((unsigned long)tail & 0x7))
120-
tail = user_backtrace(tail, entry);
121-
} else {
122-
#ifdef CONFIG_COMPAT
123-
/* AARCH32 compat mode */
124-
struct compat_frame_tail __user *tail;
125-
126-
tail = (struct compat_frame_tail __user *)regs->compat_fp - 1;
127-
128-
while ((entry->nr < entry->max_stack) &&
129-
tail && !((unsigned long)tail & 0x3))
130-
tail = compat_user_backtrace(tail, entry);
131-
#endif
132-
}
133-
}
134-
135-
static bool callchain_trace(void *data, unsigned long pc)
136-
{
137-
struct perf_callchain_entry_ctx *entry = data;
138-
return perf_callchain_store(entry, pc) == 0;
28+
arch_stack_walk_user(callchain_trace, entry, regs);
13929
}
14030

14131
void perf_callchain_kernel(struct perf_callchain_entry_ctx *entry,

arch/arm64/kernel/stacktrace.c

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,3 +324,123 @@ void show_stack(struct task_struct *tsk, unsigned long *sp, const char *loglvl)
324324
dump_backtrace(NULL, tsk, loglvl);
325325
barrier();
326326
}
327+
328+
/*
329+
* The struct defined for userspace stack frame in AARCH64 mode.
330+
*/
331+
struct frame_tail {
332+
struct frame_tail __user *fp;
333+
unsigned long lr;
334+
} __attribute__((packed));
335+
336+
/*
337+
* Get the return address for a single stackframe and return a pointer to the
338+
* next frame tail.
339+
*/
340+
static struct frame_tail __user *
341+
unwind_user_frame(struct frame_tail __user *tail, void *cookie,
342+
stack_trace_consume_fn consume_entry)
343+
{
344+
struct frame_tail buftail;
345+
unsigned long err;
346+
unsigned long lr;
347+
348+
/* Also check accessibility of one struct frame_tail beyond */
349+
if (!access_ok(tail, sizeof(buftail)))
350+
return NULL;
351+
352+
pagefault_disable();
353+
err = __copy_from_user_inatomic(&buftail, tail, sizeof(buftail));
354+
pagefault_enable();
355+
356+
if (err)
357+
return NULL;
358+
359+
lr = ptrauth_strip_user_insn_pac(buftail.lr);
360+
361+
if (!consume_entry(cookie, lr))
362+
return NULL;
363+
364+
/*
365+
* Frame pointers should strictly progress back up the stack
366+
* (towards higher addresses).
367+
*/
368+
if (tail >= buftail.fp)
369+
return NULL;
370+
371+
return buftail.fp;
372+
}
373+
374+
#ifdef CONFIG_COMPAT
375+
/*
376+
* The registers we're interested in are at the end of the variable
377+
* length saved register structure. The fp points at the end of this
378+
* structure so the address of this struct is:
379+
* (struct compat_frame_tail *)(xxx->fp)-1
380+
*
381+
* This code has been adapted from the ARM OProfile support.
382+
*/
383+
struct compat_frame_tail {
384+
compat_uptr_t fp; /* a (struct compat_frame_tail *) in compat mode */
385+
u32 sp;
386+
u32 lr;
387+
} __attribute__((packed));
388+
389+
static struct compat_frame_tail __user *
390+
unwind_compat_user_frame(struct compat_frame_tail __user *tail, void *cookie,
391+
stack_trace_consume_fn consume_entry)
392+
{
393+
struct compat_frame_tail buftail;
394+
unsigned long err;
395+
396+
/* Also check accessibility of one struct frame_tail beyond */
397+
if (!access_ok(tail, sizeof(buftail)))
398+
return NULL;
399+
400+
pagefault_disable();
401+
err = __copy_from_user_inatomic(&buftail, tail, sizeof(buftail));
402+
pagefault_enable();
403+
404+
if (err)
405+
return NULL;
406+
407+
if (!consume_entry(cookie, buftail.lr))
408+
return NULL;
409+
410+
/*
411+
* Frame pointers should strictly progress back up the stack
412+
* (towards higher addresses).
413+
*/
414+
if (tail + 1 >= (struct compat_frame_tail __user *)
415+
compat_ptr(buftail.fp))
416+
return NULL;
417+
418+
return (struct compat_frame_tail __user *)compat_ptr(buftail.fp) - 1;
419+
}
420+
#endif /* CONFIG_COMPAT */
421+
422+
423+
void arch_stack_walk_user(stack_trace_consume_fn consume_entry, void *cookie,
424+
const struct pt_regs *regs)
425+
{
426+
if (!consume_entry(cookie, regs->pc))
427+
return;
428+
429+
if (!compat_user_mode(regs)) {
430+
/* AARCH64 mode */
431+
struct frame_tail __user *tail;
432+
433+
tail = (struct frame_tail __user *)regs->regs[29];
434+
while (tail && !((unsigned long)tail & 0x7))
435+
tail = unwind_user_frame(tail, cookie, consume_entry);
436+
} else {
437+
#ifdef CONFIG_COMPAT
438+
/* AARCH32 compat mode */
439+
struct compat_frame_tail __user *tail;
440+
441+
tail = (struct compat_frame_tail __user *)regs->compat_fp - 1;
442+
while (tail && !((unsigned long)tail & 0x3))
443+
tail = unwind_compat_user_frame(tail, cookie, consume_entry);
444+
#endif
445+
}
446+
}

0 commit comments

Comments
 (0)