Skip to content

Commit 2e21d85

Browse files
committed
Merge branch 'for-next/stacktrace' into for-next/kasan
Merge in stack unwinding work to cater for 8-byte aligned stack frames which may be generated following optimisations to CONFIG_KASAN_OUTLINE. * for-next/stacktrace: arm64: stacktrace: Relax frame record alignment requirement to 8 bytes arm64: Change the on_*stack functions to take a size argument arm64: Implement stack trace termination record
2 parents c468154 + 33c222a commit 2e21d85

10 files changed

Lines changed: 70 additions & 51 deletions

File tree

arch/arm64/include/asm/processor.h

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -329,13 +329,13 @@ long get_tagged_addr_ctrl(struct task_struct *task);
329329
* of header definitions for the use of task_stack_page.
330330
*/
331331

332-
#define current_top_of_stack() \
333-
({ \
334-
struct stack_info _info; \
335-
BUG_ON(!on_accessible_stack(current, current_stack_pointer, &_info)); \
336-
_info.high; \
332+
#define current_top_of_stack() \
333+
({ \
334+
struct stack_info _info; \
335+
BUG_ON(!on_accessible_stack(current, current_stack_pointer, 1, &_info)); \
336+
_info.high; \
337337
})
338-
#define on_thread_stack() (on_task_stack(current, current_stack_pointer, NULL))
338+
#define on_thread_stack() (on_task_stack(current, current_stack_pointer, 1, NULL))
339339

340340
#endif /* __ASSEMBLY__ */
341341
#endif /* __ASM_PROCESSOR_H */

arch/arm64/include/asm/sdei.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,16 +42,17 @@ unsigned long sdei_arch_get_entry_point(int conduit);
4242

4343
struct stack_info;
4444

45-
bool _on_sdei_stack(unsigned long sp, struct stack_info *info);
46-
static inline bool on_sdei_stack(unsigned long sp,
45+
bool _on_sdei_stack(unsigned long sp, unsigned long size,
46+
struct stack_info *info);
47+
static inline bool on_sdei_stack(unsigned long sp, unsigned long size,
4748
struct stack_info *info)
4849
{
4950
if (!IS_ENABLED(CONFIG_VMAP_STACK))
5051
return false;
5152
if (!IS_ENABLED(CONFIG_ARM_SDE_INTERFACE))
5253
return false;
5354
if (in_nmi())
54-
return _on_sdei_stack(sp, info);
55+
return _on_sdei_stack(sp, size, info);
5556

5657
return false;
5758
}

arch/arm64/include/asm/stacktrace.h

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -69,14 +69,14 @@ extern void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk,
6969

7070
DECLARE_PER_CPU(unsigned long *, irq_stack_ptr);
7171

72-
static inline bool on_stack(unsigned long sp, unsigned long low,
73-
unsigned long high, enum stack_type type,
74-
struct stack_info *info)
72+
static inline bool on_stack(unsigned long sp, unsigned long size,
73+
unsigned long low, unsigned long high,
74+
enum stack_type type, struct stack_info *info)
7575
{
7676
if (!low)
7777
return false;
7878

79-
if (sp < low || sp >= high)
79+
if (sp < low || sp + size < sp || sp + size > high)
8080
return false;
8181

8282
if (info) {
@@ -87,38 +87,38 @@ static inline bool on_stack(unsigned long sp, unsigned long low,
8787
return true;
8888
}
8989

90-
static inline bool on_irq_stack(unsigned long sp,
90+
static inline bool on_irq_stack(unsigned long sp, unsigned long size,
9191
struct stack_info *info)
9292
{
9393
unsigned long low = (unsigned long)raw_cpu_read(irq_stack_ptr);
9494
unsigned long high = low + IRQ_STACK_SIZE;
9595

96-
return on_stack(sp, low, high, STACK_TYPE_IRQ, info);
96+
return on_stack(sp, size, low, high, STACK_TYPE_IRQ, info);
9797
}
9898

9999
static inline bool on_task_stack(const struct task_struct *tsk,
100-
unsigned long sp,
100+
unsigned long sp, unsigned long size,
101101
struct stack_info *info)
102102
{
103103
unsigned long low = (unsigned long)task_stack_page(tsk);
104104
unsigned long high = low + THREAD_SIZE;
105105

106-
return on_stack(sp, low, high, STACK_TYPE_TASK, info);
106+
return on_stack(sp, size, low, high, STACK_TYPE_TASK, info);
107107
}
108108

109109
#ifdef CONFIG_VMAP_STACK
110110
DECLARE_PER_CPU(unsigned long [OVERFLOW_STACK_SIZE/sizeof(long)], overflow_stack);
111111

112-
static inline bool on_overflow_stack(unsigned long sp,
112+
static inline bool on_overflow_stack(unsigned long sp, unsigned long size,
113113
struct stack_info *info)
114114
{
115115
unsigned long low = (unsigned long)raw_cpu_ptr(overflow_stack);
116116
unsigned long high = low + OVERFLOW_STACK_SIZE;
117117

118-
return on_stack(sp, low, high, STACK_TYPE_OVERFLOW, info);
118+
return on_stack(sp, size, low, high, STACK_TYPE_OVERFLOW, info);
119119
}
120120
#else
121-
static inline bool on_overflow_stack(unsigned long sp,
121+
static inline bool on_overflow_stack(unsigned long sp, unsigned long size,
122122
struct stack_info *info) { return false; }
123123
#endif
124124

@@ -128,21 +128,21 @@ static inline bool on_overflow_stack(unsigned long sp,
128128
* context.
129129
*/
130130
static inline bool on_accessible_stack(const struct task_struct *tsk,
131-
unsigned long sp,
131+
unsigned long sp, unsigned long size,
132132
struct stack_info *info)
133133
{
134134
if (info)
135135
info->type = STACK_TYPE_UNKNOWN;
136136

137-
if (on_task_stack(tsk, sp, info))
137+
if (on_task_stack(tsk, sp, size, info))
138138
return true;
139139
if (tsk != current || preemptible())
140140
return false;
141-
if (on_irq_stack(sp, info))
141+
if (on_irq_stack(sp, size, info))
142142
return true;
143-
if (on_overflow_stack(sp, info))
143+
if (on_overflow_stack(sp, size, info))
144144
return true;
145-
if (on_sdei_stack(sp, info))
145+
if (on_sdei_stack(sp, size, info))
146146
return true;
147147

148148
return false;

arch/arm64/kernel/entry.S

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,7 @@ alternative_else_nop_endif
285285
stp lr, x21, [sp, #S_LR]
286286

287287
/*
288-
* For exceptions from EL0, create a terminal frame record.
288+
* For exceptions from EL0, create a final frame record.
289289
* For exceptions from EL1, create a synthetic frame record so the
290290
* interrupted code shows up in the backtrace.
291291
*/

arch/arm64/kernel/head.S

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include <asm/asm_pointer_auth.h>
1717
#include <asm/assembler.h>
1818
#include <asm/boot.h>
19+
#include <asm/bug.h>
1920
#include <asm/ptrace.h>
2021
#include <asm/asm-offsets.h>
2122
#include <asm/cache.h>
@@ -393,6 +394,18 @@ SYM_FUNC_START_LOCAL(__create_page_tables)
393394
ret x28
394395
SYM_FUNC_END(__create_page_tables)
395396

397+
/*
398+
* Create a final frame record at task_pt_regs(current)->stackframe, so
399+
* that the unwinder can identify the final frame record of any task by
400+
* its location in the task stack. We reserve the entire pt_regs space
401+
* for consistency with user tasks and kthreads.
402+
*/
403+
.macro setup_final_frame
404+
sub sp, sp, #PT_REGS_SIZE
405+
stp xzr, xzr, [sp, #S_STACKFRAME]
406+
add x29, sp, #S_STACKFRAME
407+
.endm
408+
396409
/*
397410
* The following fragment of code is executed with the MMU enabled.
398411
*
@@ -447,9 +460,9 @@ SYM_FUNC_START_LOCAL(__primary_switched)
447460
#endif
448461
bl switch_to_vhe // Prefer VHE if possible
449462
add sp, sp, #16
450-
mov x29, #0
451-
mov x30, #0
452-
b start_kernel
463+
setup_final_frame
464+
bl start_kernel
465+
ASM_BUG()
453466
SYM_FUNC_END(__primary_switched)
454467

455468
.pushsection ".rodata", "a"
@@ -639,14 +652,14 @@ SYM_FUNC_START_LOCAL(__secondary_switched)
639652
cbz x2, __secondary_too_slow
640653
msr sp_el0, x2
641654
scs_load x2, x3
642-
mov x29, #0
643-
mov x30, #0
655+
setup_final_frame
644656

645657
#ifdef CONFIG_ARM64_PTR_AUTH
646658
ptrauth_keys_init_cpu x2, x3, x4, x5
647659
#endif
648660

649-
b secondary_start_kernel
661+
bl secondary_start_kernel
662+
ASM_BUG()
650663
SYM_FUNC_END(__secondary_switched)
651664

652665
SYM_FUNC_START_LOCAL(__secondary_too_slow)

arch/arm64/kernel/perf_callchain.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ void perf_callchain_user(struct perf_callchain_entry_ctx *entry,
116116
tail = (struct frame_tail __user *)regs->regs[29];
117117

118118
while (entry->nr < entry->max_stack &&
119-
tail && !((unsigned long)tail & 0xf))
119+
tail && !((unsigned long)tail & 0x7))
120120
tail = user_backtrace(tail, entry);
121121
} else {
122122
#ifdef CONFIG_COMPAT

arch/arm64/kernel/process.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -435,6 +435,11 @@ int copy_thread(unsigned long clone_flags, unsigned long stack_start,
435435
}
436436
p->thread.cpu_context.pc = (unsigned long)ret_from_fork;
437437
p->thread.cpu_context.sp = (unsigned long)childregs;
438+
/*
439+
* For the benefit of the unwinder, set up childregs->stackframe
440+
* as the final frame for the new task.
441+
*/
442+
p->thread.cpu_context.fp = (unsigned long)childregs->stackframe;
438443

439444
ptrace_hw_copy_thread(p);
440445

arch/arm64/kernel/ptrace.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ static bool regs_within_kernel_stack(struct pt_regs *regs, unsigned long addr)
122122
{
123123
return ((addr & ~(THREAD_SIZE - 1)) ==
124124
(kernel_stack_pointer(regs) & ~(THREAD_SIZE - 1))) ||
125-
on_irq_stack(addr, NULL);
125+
on_irq_stack(addr, sizeof(unsigned long), NULL);
126126
}
127127

128128
/**

arch/arm64/kernel/sdei.c

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -162,31 +162,33 @@ static int init_sdei_scs(void)
162162
return err;
163163
}
164164

165-
static bool on_sdei_normal_stack(unsigned long sp, struct stack_info *info)
165+
static bool on_sdei_normal_stack(unsigned long sp, unsigned long size,
166+
struct stack_info *info)
166167
{
167168
unsigned long low = (unsigned long)raw_cpu_read(sdei_stack_normal_ptr);
168169
unsigned long high = low + SDEI_STACK_SIZE;
169170

170-
return on_stack(sp, low, high, STACK_TYPE_SDEI_NORMAL, info);
171+
return on_stack(sp, size, low, high, STACK_TYPE_SDEI_NORMAL, info);
171172
}
172173

173-
static bool on_sdei_critical_stack(unsigned long sp, struct stack_info *info)
174+
static bool on_sdei_critical_stack(unsigned long sp, unsigned long size,
175+
struct stack_info *info)
174176
{
175177
unsigned long low = (unsigned long)raw_cpu_read(sdei_stack_critical_ptr);
176178
unsigned long high = low + SDEI_STACK_SIZE;
177179

178-
return on_stack(sp, low, high, STACK_TYPE_SDEI_CRITICAL, info);
180+
return on_stack(sp, size, low, high, STACK_TYPE_SDEI_CRITICAL, info);
179181
}
180182

181-
bool _on_sdei_stack(unsigned long sp, struct stack_info *info)
183+
bool _on_sdei_stack(unsigned long sp, unsigned long size, struct stack_info *info)
182184
{
183185
if (!IS_ENABLED(CONFIG_VMAP_STACK))
184186
return false;
185187

186-
if (on_sdei_critical_stack(sp, info))
188+
if (on_sdei_critical_stack(sp, size, info))
187189
return true;
188190

189-
if (on_sdei_normal_stack(sp, info))
191+
if (on_sdei_normal_stack(sp, size, info))
190192
return true;
191193

192194
return false;

arch/arm64/kernel/stacktrace.c

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -68,13 +68,17 @@ int notrace unwind_frame(struct task_struct *tsk, struct stackframe *frame)
6868
unsigned long fp = frame->fp;
6969
struct stack_info info;
7070

71-
if (fp & 0xf)
72-
return -EINVAL;
73-
7471
if (!tsk)
7572
tsk = current;
7673

77-
if (!on_accessible_stack(tsk, fp, &info))
74+
/* Final frame; nothing to unwind */
75+
if (fp == (unsigned long)task_pt_regs(tsk)->stackframe)
76+
return -ENOENT;
77+
78+
if (fp & 0x7)
79+
return -EINVAL;
80+
81+
if (!on_accessible_stack(tsk, fp, 16, &info))
7882
return -EINVAL;
7983

8084
if (test_bit(info.type, frame->stacks_done))
@@ -128,12 +132,6 @@ int notrace unwind_frame(struct task_struct *tsk, struct stackframe *frame)
128132

129133
frame->pc = ptrauth_strip_insn_pac(frame->pc);
130134

131-
/*
132-
* This is a terminal record, so we have finished unwinding.
133-
*/
134-
if (!frame->fp && !frame->pc)
135-
return -ENOENT;
136-
137135
return 0;
138136
}
139137
NOKPROBE_SYMBOL(unwind_frame);

0 commit comments

Comments
 (0)