Skip to content

Commit

Permalink
arm64: Change the on_*stack functions to take a size argument
Browse files Browse the repository at this point in the history
unwind_frame() was previously implicitly checking that the frame
record is in bounds of the stack by enforcing that FP is both aligned
to 16 and in bounds of the stack. Once the FP alignment requirement
is relaxed to 8 this will not be sufficient because it does not
account for the case where FP points to 8 bytes before the end of the
stack.

Make the check explicit by changing the on_*stack functions to take a
size argument and adjusting the callers to pass the appropriate sizes.

Signed-off-by: Peter Collingbourne <[email protected]>
Link: https://linux-review.googlesource.com/id/Ib7a3eb3eea41b0687ffaba045ceb2012d077d8b4
Reviewed-by: Mark Rutland <[email protected]>
Tested-by: Mark Rutland <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
Signed-off-by: Will Deacon <[email protected]>
  • Loading branch information
pcc authored and willdeacon committed May 26, 2021
1 parent 7d7b720 commit 76734d2
Show file tree
Hide file tree
Showing 6 changed files with 37 additions and 34 deletions.
12 changes: 6 additions & 6 deletions arch/arm64/include/asm/processor.h
Original file line number Diff line number Diff line change
Expand Up @@ -329,13 +329,13 @@ long get_tagged_addr_ctrl(struct task_struct *task);
* of header definitions for the use of task_stack_page.
*/

#define current_top_of_stack() \
({ \
struct stack_info _info; \
BUG_ON(!on_accessible_stack(current, current_stack_pointer, &_info)); \
_info.high; \
#define current_top_of_stack() \
({ \
struct stack_info _info; \
BUG_ON(!on_accessible_stack(current, current_stack_pointer, 1, &_info)); \
_info.high; \
})
#define on_thread_stack() (on_task_stack(current, current_stack_pointer, NULL))
#define on_thread_stack() (on_task_stack(current, current_stack_pointer, 1, NULL))

#endif /* __ASSEMBLY__ */
#endif /* __ASM_PROCESSOR_H */
7 changes: 4 additions & 3 deletions arch/arm64/include/asm/sdei.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,16 +42,17 @@ unsigned long sdei_arch_get_entry_point(int conduit);

struct stack_info;

bool _on_sdei_stack(unsigned long sp, struct stack_info *info);
static inline bool on_sdei_stack(unsigned long sp,
bool _on_sdei_stack(unsigned long sp, unsigned long size,
struct stack_info *info);
static inline bool on_sdei_stack(unsigned long sp, unsigned long size,
struct stack_info *info)
{
if (!IS_ENABLED(CONFIG_VMAP_STACK))
return false;
if (!IS_ENABLED(CONFIG_ARM_SDE_INTERFACE))
return false;
if (in_nmi())
return _on_sdei_stack(sp, info);
return _on_sdei_stack(sp, size, info);

return false;
}
Expand Down
32 changes: 16 additions & 16 deletions arch/arm64/include/asm/stacktrace.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,14 +69,14 @@ extern void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk,

DECLARE_PER_CPU(unsigned long *, irq_stack_ptr);

static inline bool on_stack(unsigned long sp, unsigned long low,
unsigned long high, enum stack_type type,
struct stack_info *info)
static inline bool on_stack(unsigned long sp, unsigned long size,
unsigned long low, unsigned long high,
enum stack_type type, struct stack_info *info)
{
if (!low)
return false;

if (sp < low || sp >= high)
if (sp < low || sp + size < sp || sp + size > high)
return false;

if (info) {
Expand All @@ -87,38 +87,38 @@ static inline bool on_stack(unsigned long sp, unsigned long low,
return true;
}

static inline bool on_irq_stack(unsigned long sp,
static inline bool on_irq_stack(unsigned long sp, unsigned long size,
struct stack_info *info)
{
unsigned long low = (unsigned long)raw_cpu_read(irq_stack_ptr);
unsigned long high = low + IRQ_STACK_SIZE;

return on_stack(sp, low, high, STACK_TYPE_IRQ, info);
return on_stack(sp, size, low, high, STACK_TYPE_IRQ, info);
}

static inline bool on_task_stack(const struct task_struct *tsk,
unsigned long sp,
unsigned long sp, unsigned long size,
struct stack_info *info)
{
unsigned long low = (unsigned long)task_stack_page(tsk);
unsigned long high = low + THREAD_SIZE;

return on_stack(sp, low, high, STACK_TYPE_TASK, info);
return on_stack(sp, size, low, high, STACK_TYPE_TASK, info);
}

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

static inline bool on_overflow_stack(unsigned long sp,
static inline bool on_overflow_stack(unsigned long sp, unsigned long size,
struct stack_info *info)
{
unsigned long low = (unsigned long)raw_cpu_ptr(overflow_stack);
unsigned long high = low + OVERFLOW_STACK_SIZE;

return on_stack(sp, low, high, STACK_TYPE_OVERFLOW, info);
return on_stack(sp, size, low, high, STACK_TYPE_OVERFLOW, info);
}
#else
static inline bool on_overflow_stack(unsigned long sp,
static inline bool on_overflow_stack(unsigned long sp, unsigned long size,
struct stack_info *info) { return false; }
#endif

Expand All @@ -128,21 +128,21 @@ static inline bool on_overflow_stack(unsigned long sp,
* context.
*/
static inline bool on_accessible_stack(const struct task_struct *tsk,
unsigned long sp,
unsigned long sp, unsigned long size,
struct stack_info *info)
{
if (info)
info->type = STACK_TYPE_UNKNOWN;

if (on_task_stack(tsk, sp, info))
if (on_task_stack(tsk, sp, size, info))
return true;
if (tsk != current || preemptible())
return false;
if (on_irq_stack(sp, info))
if (on_irq_stack(sp, size, info))
return true;
if (on_overflow_stack(sp, info))
if (on_overflow_stack(sp, size, info))
return true;
if (on_sdei_stack(sp, info))
if (on_sdei_stack(sp, size, info))
return true;

return false;
Expand Down
2 changes: 1 addition & 1 deletion arch/arm64/kernel/ptrace.c
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ static bool regs_within_kernel_stack(struct pt_regs *regs, unsigned long addr)
{
return ((addr & ~(THREAD_SIZE - 1)) ==
(kernel_stack_pointer(regs) & ~(THREAD_SIZE - 1))) ||
on_irq_stack(addr, NULL);
on_irq_stack(addr, sizeof(unsigned long), NULL);
}

/**
Expand Down
16 changes: 9 additions & 7 deletions arch/arm64/kernel/sdei.c
Original file line number Diff line number Diff line change
Expand Up @@ -162,31 +162,33 @@ static int init_sdei_scs(void)
return err;
}

static bool on_sdei_normal_stack(unsigned long sp, struct stack_info *info)
static bool on_sdei_normal_stack(unsigned long sp, unsigned long size,
struct stack_info *info)
{
unsigned long low = (unsigned long)raw_cpu_read(sdei_stack_normal_ptr);
unsigned long high = low + SDEI_STACK_SIZE;

return on_stack(sp, low, high, STACK_TYPE_SDEI_NORMAL, info);
return on_stack(sp, size, low, high, STACK_TYPE_SDEI_NORMAL, info);
}

static bool on_sdei_critical_stack(unsigned long sp, struct stack_info *info)
static bool on_sdei_critical_stack(unsigned long sp, unsigned long size,
struct stack_info *info)
{
unsigned long low = (unsigned long)raw_cpu_read(sdei_stack_critical_ptr);
unsigned long high = low + SDEI_STACK_SIZE;

return on_stack(sp, low, high, STACK_TYPE_SDEI_CRITICAL, info);
return on_stack(sp, size, low, high, STACK_TYPE_SDEI_CRITICAL, info);
}

bool _on_sdei_stack(unsigned long sp, struct stack_info *info)
bool _on_sdei_stack(unsigned long sp, unsigned long size, struct stack_info *info)
{
if (!IS_ENABLED(CONFIG_VMAP_STACK))
return false;

if (on_sdei_critical_stack(sp, info))
if (on_sdei_critical_stack(sp, size, info))
return true;

if (on_sdei_normal_stack(sp, info))
if (on_sdei_normal_stack(sp, size, info))
return true;

return false;
Expand Down
2 changes: 1 addition & 1 deletion arch/arm64/kernel/stacktrace.c
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ int notrace unwind_frame(struct task_struct *tsk, struct stackframe *frame)
if (fp & 0xf)
return -EINVAL;

if (!on_accessible_stack(tsk, fp, &info))
if (!on_accessible_stack(tsk, fp, 16, &info))
return -EINVAL;

if (test_bit(info.type, frame->stacks_done))
Expand Down

0 comments on commit 76734d2

Please sign in to comment.