Skip to content

Commit

Permalink
arm64: stacktrace: split unwind_consume_stack()
Browse files Browse the repository at this point in the history
When unwinding stacks, we use unwind_consume_stack() to both find
whether an object (e.g. a frame record) is on an accessible stack *and*
to update the stack boundaries. This works fine today since we only care
about one type of object which does not overlap other objects.

In subsequent patches we'll want to check whether an object (e.g a frame
record) is on the stack and follow this up by accessing a larger object
containing the first (e.g. a pt_regs with an embedded frame record).

To make that pattern easier to implement, this patch reworks
unwind_find_next_stack() and unwind_consume_stack() so that the former
can be used to check if an object is on any accessible stack, and the
latter is purely used to update the stack boundaries.

As unwind_find_next_stack() is modified to also check the stack
currently being unwound, it is renamed to unwind_find_stack().

There should be no functional change as a result of this patch.

Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Reviewed-by: Mark Brown <broonie@kernel.org>
Reviewed-by: Miroslav Benes <mbenes@suse.cz>
Reviewed-by: Puranjay Mohan <puranjay12@gmail.com>
Cc: Ard Biesheuvel <ardb@kernel.org>
Cc: Josh Poimboeuf <jpoimboe@kernel.org>
Cc: Kalesh Singh <kaleshsingh@google.com>
Cc: Madhavan T. Venkataraman <madvenka@linux.microsoft.com>
Cc: Marc Zyngier <maz@kernel.org>
Cc: Will Deacon <will@kernel.org>
Link: https://lore.kernel.org/r/20241017092538.1859841-10-mark.rutland@arm.com
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
  • Loading branch information
Mark Rutland authored and ctmarinas committed Oct 17, 2024
1 parent 8094df1 commit f05a4a4
Showing 1 changed file with 39 additions and 29 deletions.
68 changes: 39 additions & 29 deletions arch/arm64/include/asm/stacktrace/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,27 @@ static inline void unwind_init_common(struct unwind_state *state)
state->stack = stackinfo_get_unknown();
}

static struct stack_info *unwind_find_next_stack(const struct unwind_state *state,
unsigned long sp,
unsigned long size)
/**
* unwind_find_stack() - Find the accessible stack which entirely contains an
* object.
*
* @state: the current unwind state.
* @sp: the base address of the object.
* @size: the size of the object.
*
* Return: a pointer to the relevant stack_info if found; NULL otherwise.
*/
static struct stack_info *unwind_find_stack(struct unwind_state *state,
unsigned long sp,
unsigned long size)
{
for (int i = 0; i < state->nr_stacks; i++) {
struct stack_info *info = &state->stacks[i];
struct stack_info *info = &state->stack;

if (stackinfo_on_stack(info, sp, size))
return info;

for (int i = 0; i < state->nr_stacks; i++) {
info = &state->stacks[i];
if (stackinfo_on_stack(info, sp, size))
return info;
}
Expand All @@ -75,36 +89,31 @@ static struct stack_info *unwind_find_next_stack(const struct unwind_state *stat
}

/**
* unwind_consume_stack() - Check if an object is on an accessible stack,
* updating stack boundaries so that future unwind steps cannot consume this
* object again.
* unwind_consume_stack() - Update stack boundaries so that future unwind steps
* cannot consume this object again.
*
* @state: the current unwind state.
* @info: the stack_info of the stack containing the object.
* @sp: the base address of the object.
* @size: the size of the object.
*
* Return: 0 upon success, an error code otherwise.
*/
static inline int unwind_consume_stack(struct unwind_state *state,
unsigned long sp,
unsigned long size)
static inline void unwind_consume_stack(struct unwind_state *state,
struct stack_info *info,
unsigned long sp,
unsigned long size)
{
struct stack_info *next;

if (stackinfo_on_stack(&state->stack, sp, size))
goto found;

next = unwind_find_next_stack(state, sp, size);
if (!next)
return -EINVAL;
struct stack_info tmp;

/*
* Stack transitions are strictly one-way, and once we've
* transitioned from one stack to another, it's never valid to
* unwind back to the old stack.
*
* Remove the current stack from the list of stacks so that it cannot
* be found on a subsequent transition.
* Destroy the old stack info so that it cannot be found upon a
* subsequent transition. If the stack has not changed, we'll
* immediately restore the current stack info.
*
* Note that stacks can nest in several valid orders, e.g.
*
Expand All @@ -115,16 +124,15 @@ static inline int unwind_consume_stack(struct unwind_state *state,
* ... so we do not check the specific order of stack
* transitions.
*/
state->stack = *next;
*next = stackinfo_get_unknown();
tmp = *info;
*info = stackinfo_get_unknown();
state->stack = tmp;

found:
/*
* Future unwind steps can only consume stack above this frame record.
* Update the current stack to start immediately above it.
*/
state->stack.low = sp + size;
return 0;
}

/**
Expand All @@ -137,16 +145,18 @@ static inline int unwind_consume_stack(struct unwind_state *state,
static inline int
unwind_next_frame_record(struct unwind_state *state)
{
struct stack_info *info;
struct frame_record *record;
unsigned long fp = state->fp;
int err;

if (fp & 0x7)
return -EINVAL;

err = unwind_consume_stack(state, fp, sizeof(*record));
if (err)
return err;
info = unwind_find_stack(state, fp, sizeof(*record));
if (!info)
return -EINVAL;

unwind_consume_stack(state, info, fp, sizeof(*record));

/*
* Record this frame record's values.
Expand Down

0 comments on commit f05a4a4

Please sign in to comment.