Commit be63c647 authored by Kalesh Singh's avatar Kalesh Singh Committed by Marc Zyngier
Browse files

arm64: stacktrace: Factor out unwind_next_common()



Move common unwind_next logic to stacktrace/common.h. This allows
reusing the code in the implementation the nVHE hypervisor stack
unwinder, later in this series.

Signed-off-by: default avatarKalesh Singh <kaleshsingh@google.com>
Reviewed-by: default avatarFuad Tabba <tabba@google.com>
Reviewed-by: default avatarMark Brown <broonie@kernel.org>
Tested-by: default avatarFuad Tabba <tabba@google.com>
Signed-off-by: default avatarMarc Zyngier <maz@kernel.org>
Link: https://lore.kernel.org/r/20220726073750.3219117-4-kaleshsingh@google.com
parent 15a59f19
Loading
Loading
Loading
Loading
+50 −0
Original line number Diff line number Diff line
@@ -65,6 +65,10 @@ struct unwind_state {
static inline bool on_overflow_stack(unsigned long sp, unsigned long size,
				     struct stack_info *info);

static inline bool on_accessible_stack(const struct task_struct *tsk,
				       unsigned long sp, unsigned long size,
				       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)
@@ -120,4 +124,50 @@ static inline void unwind_init_common(struct unwind_state *state,
	state->prev_type = STACK_TYPE_UNKNOWN;
}

static inline int unwind_next_common(struct unwind_state *state,
				     struct stack_info *info)
{
	struct task_struct *tsk = state->task;
	unsigned long fp = state->fp;

	if (fp & 0x7)
		return -EINVAL;

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

	if (test_bit(info->type, state->stacks_done))
		return -EINVAL;

	/*
	 * As stacks grow downward, any valid record on the same stack must be
	 * at a strictly higher address than the prior record.
	 *
	 * Stacks can nest in several valid orders, e.g.
	 *
	 * TASK -> IRQ -> OVERFLOW -> SDEI_NORMAL
	 * TASK -> SDEI_NORMAL -> SDEI_CRITICAL -> OVERFLOW
	 *
	 * ... but the nesting itself is strict. Once we transition from one
	 * stack to another, it's never valid to unwind back to that first
	 * stack.
	 */
	if (info->type == state->prev_type) {
		if (fp <= state->prev_fp)
			return -EINVAL;
	} else {
		__set_bit(state->prev_type, state->stacks_done);
	}

	/*
	 * Record this frame record's values and location. The prev_fp and
	 * prev_type are only meaningful to the next unwind_next() invocation.
	 */
	state->fp = READ_ONCE(*(unsigned long *)(fp));
	state->pc = READ_ONCE(*(unsigned long *)(fp + 8));
	state->prev_fp = fp;
	state->prev_type = info->type;

	return 0;
}
#endif	/* __ASM_STACKTRACE_COMMON_H */
+4 −37
Original line number Diff line number Diff line
@@ -81,48 +81,15 @@ static int notrace unwind_next(struct unwind_state *state)
	struct task_struct *tsk = state->task;
	unsigned long fp = state->fp;
	struct stack_info info;
	int err;

	/* Final frame; nothing to unwind */
	if (fp == (unsigned long)task_pt_regs(tsk)->stackframe)
		return -ENOENT;

	if (fp & 0x7)
		return -EINVAL;

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

	if (test_bit(info.type, state->stacks_done))
		return -EINVAL;

	/*
	 * As stacks grow downward, any valid record on the same stack must be
	 * at a strictly higher address than the prior record.
	 *
	 * Stacks can nest in several valid orders, e.g.
	 *
	 * TASK -> IRQ -> OVERFLOW -> SDEI_NORMAL
	 * TASK -> SDEI_NORMAL -> SDEI_CRITICAL -> OVERFLOW
	 *
	 * ... but the nesting itself is strict. Once we transition from one
	 * stack to another, it's never valid to unwind back to that first
	 * stack.
	 */
	if (info.type == state->prev_type) {
		if (fp <= state->prev_fp)
			return -EINVAL;
	} else {
		__set_bit(state->prev_type, state->stacks_done);
	}

	/*
	 * Record this frame record's values and location. The prev_fp and
	 * prev_type are only meaningful to the next unwind_next() invocation.
	 */
	state->fp = READ_ONCE(*(unsigned long *)(fp));
	state->pc = READ_ONCE(*(unsigned long *)(fp + 8));
	state->prev_fp = fp;
	state->prev_type = info.type;
	err = unwind_next_common(state, &info);
	if (err)
		return err;

	state->pc = ptrauth_strip_insn_pac(state->pc);