Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8a1ccfbc authored by Laura Abbott's avatar Laura Abbott Committed by Will Deacon
Browse files

arm64: Add stack information to on_accessible_stack



In preparation for enabling the stackleak plugin on arm64,
we need a way to get the bounds of the current stack. Extend
on_accessible_stack to get this information.

Acked-by: default avatarAlexander Popov <alex.popov@linux.com>
Reviewed-by: default avatarMark Rutland <mark.rutland@arm.com>
Signed-off-by: default avatarLaura Abbott <labbott@redhat.com>
[will: folded in fix for allmodconfig build breakage w/ sdei]
Signed-off-by: default avatarWill Deacon <will.deacon@arm.com>
parent 2c870e61
Loading
Loading
Loading
Loading
+6 −3
Original line number Original line Diff line number Diff line
@@ -40,15 +40,18 @@ asmlinkage unsigned long __sdei_handler(struct pt_regs *regs,
unsigned long sdei_arch_get_entry_point(int conduit);
unsigned long sdei_arch_get_entry_point(int conduit);
#define sdei_arch_get_entry_point(x)	sdei_arch_get_entry_point(x)
#define sdei_arch_get_entry_point(x)	sdei_arch_get_entry_point(x)


bool _on_sdei_stack(unsigned long sp);
struct stack_info;
static inline bool on_sdei_stack(unsigned long sp)

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


	return false;
	return false;
}
}
+61 −12
Original line number Original line Diff line number Diff line
@@ -32,6 +32,21 @@ struct stackframe {
#endif
#endif
};
};


enum stack_type {
	STACK_TYPE_UNKNOWN,
	STACK_TYPE_TASK,
	STACK_TYPE_IRQ,
	STACK_TYPE_OVERFLOW,
	STACK_TYPE_SDEI_NORMAL,
	STACK_TYPE_SDEI_CRITICAL,
};

struct stack_info {
	unsigned long low;
	unsigned long high;
	enum stack_type type;
};

extern int unwind_frame(struct task_struct *tsk, struct stackframe *frame);
extern int unwind_frame(struct task_struct *tsk, struct stackframe *frame);
extern void walk_stackframe(struct task_struct *tsk, struct stackframe *frame,
extern void walk_stackframe(struct task_struct *tsk, struct stackframe *frame,
			    int (*fn)(struct stackframe *, void *), void *data);
			    int (*fn)(struct stackframe *, void *), void *data);
@@ -39,7 +54,8 @@ extern void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk);


DECLARE_PER_CPU(unsigned long *, irq_stack_ptr);
DECLARE_PER_CPU(unsigned long *, irq_stack_ptr);


static inline bool on_irq_stack(unsigned long sp)
static inline bool on_irq_stack(unsigned long sp,
				struct stack_info *info)
{
{
	unsigned long low = (unsigned long)raw_cpu_read(irq_stack_ptr);
	unsigned long low = (unsigned long)raw_cpu_read(irq_stack_ptr);
	unsigned long high = low + IRQ_STACK_SIZE;
	unsigned long high = low + IRQ_STACK_SIZE;
@@ -47,46 +63,79 @@ static inline bool on_irq_stack(unsigned long sp)
	if (!low)
	if (!low)
		return false;
		return false;


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

	if (info) {
		info->low = low;
		info->high = high;
		info->type = STACK_TYPE_IRQ;
	}
	}


static inline bool on_task_stack(struct task_struct *tsk, unsigned long sp)
	return true;
}

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


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

	if (info) {
		info->low = low;
		info->high = high;
		info->type = STACK_TYPE_TASK;
	}

	return true;
}
}


#ifdef CONFIG_VMAP_STACK
#ifdef CONFIG_VMAP_STACK
DECLARE_PER_CPU(unsigned long [OVERFLOW_STACK_SIZE/sizeof(long)], overflow_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,
				struct stack_info *info)
{
{
	unsigned long low = (unsigned long)raw_cpu_ptr(overflow_stack);
	unsigned long low = (unsigned long)raw_cpu_ptr(overflow_stack);
	unsigned long high = low + OVERFLOW_STACK_SIZE;
	unsigned long high = low + OVERFLOW_STACK_SIZE;


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

	if (info) {
		info->low = low;
		info->high = high;
		info->type = STACK_TYPE_OVERFLOW;
	}

	return true;
}
}
#else
#else
static inline bool on_overflow_stack(unsigned long sp) { return false; }
static inline bool on_overflow_stack(unsigned long sp,
			struct stack_info *info) { return false; }
#endif
#endif



/*
/*
 * We can only safely access per-cpu stacks from current in a non-preemptible
 * We can only safely access per-cpu stacks from current in a non-preemptible
 * context.
 * context.
 */
 */
static inline bool on_accessible_stack(struct task_struct *tsk, unsigned long sp)
static inline bool on_accessible_stack(struct task_struct *tsk,
					unsigned long sp,
					struct stack_info *info)
{
{
	if (on_task_stack(tsk, sp))
	if (on_task_stack(tsk, sp, info))
		return true;
		return true;
	if (tsk != current || preemptible())
	if (tsk != current || preemptible())
		return false;
		return false;
	if (on_irq_stack(sp))
	if (on_irq_stack(sp, info))
		return true;
		return true;
	if (on_overflow_stack(sp))
	if (on_overflow_stack(sp, info))
		return true;
		return true;
	if (on_sdei_stack(sp))
	if (on_sdei_stack(sp, info))
		return true;
		return true;


	return false;
	return false;
+1 −1
Original line number Original line Diff line number Diff line
@@ -132,7 +132,7 @@ static bool regs_within_kernel_stack(struct pt_regs *regs, unsigned long addr)
{
{
	return ((addr & ~(THREAD_SIZE - 1))  ==
	return ((addr & ~(THREAD_SIZE - 1))  ==
		(kernel_stack_pointer(regs) & ~(THREAD_SIZE - 1))) ||
		(kernel_stack_pointer(regs) & ~(THREAD_SIZE - 1))) ||
		on_irq_stack(addr);
		on_irq_stack(addr, NULL);
}
}


/**
/**
+42 −9
Original line number Original line Diff line number Diff line
@@ -13,6 +13,7 @@
#include <asm/mmu.h>
#include <asm/mmu.h>
#include <asm/ptrace.h>
#include <asm/ptrace.h>
#include <asm/sections.h>
#include <asm/sections.h>
#include <asm/stacktrace.h>
#include <asm/sysreg.h>
#include <asm/sysreg.h>
#include <asm/vmap_stack.h>
#include <asm/vmap_stack.h>


@@ -88,23 +89,55 @@ static int init_sdei_stacks(void)
	return err;
	return err;
}
}


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


	if (!IS_ENABLED(CONFIG_VMAP_STACK))
	if (sp < low || sp >= high)
		return false;

	if (info) {
		info->low = low;
		info->high = high;
		info->type = STACK_TYPE_SDEI_NORMAL;
	}

	return true;
}

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

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


	low = (unsigned long)raw_cpu_read(sdei_stack_critical_ptr);
	if (info) {
	high = low + SDEI_STACK_SIZE;
		info->low = low;
		info->high = high;
		info->type = STACK_TYPE_SDEI_CRITICAL;
	}

	return true;
}

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


	if (low <= sp && sp < high)
	if (on_sdei_critical_stack(sp, info))
		return true;
		return true;


	low = (unsigned long)raw_cpu_read(sdei_stack_normal_ptr);
	if (on_sdei_normal_stack(sp, info))
	high = low + SDEI_STACK_SIZE;
		return true;


	return (low <= sp && sp < high);
	return false;
}
}


unsigned long sdei_arch_get_entry_point(int conduit)
unsigned long sdei_arch_get_entry_point(int conduit)
+1 −1
Original line number Original line Diff line number Diff line
@@ -50,7 +50,7 @@ int notrace unwind_frame(struct task_struct *tsk, struct stackframe *frame)
	if (!tsk)
	if (!tsk)
		tsk = current;
		tsk = current;


	if (!on_accessible_stack(tsk, fp))
	if (!on_accessible_stack(tsk, fp, NULL))
		return -EINVAL;
		return -EINVAL;


	frame->fp = READ_ONCE_NOCHECK(*(unsigned long *)(fp));
	frame->fp = READ_ONCE_NOCHECK(*(unsigned long *)(fp));