Skip to content

Commit 805f13e

Browse files
liu-song-6ctmarinas
authored andcommitted
arm64: stacktrace: Implement arch_stack_walk_reliable()
Add arch_stack_walk_reliable(), which will be used during kernel live patching to detect when threads have completed executing old versions of functions. Note that arch_stack_walk_reliable() only needs to guarantee that it returns an error code when it cannot provide a reliable stacktrace. It is not required to provide a reliable stacktrace in all scenarios so long as it returns said error code. At present we can only reliably unwind up to an exception boundary. In future we should be able to improve this with additional data from the compiler (e.g. sframe). Signed-off-by: Song Liu <[email protected]> Link: https://lore.kernel.org/r/[email protected] [ Mark: Simplify logic, clarify commit message ] Signed-off-by: Mark Rutland <[email protected]> Cc: Andrea della Porta <[email protected]> Cc: Breno Leitao <[email protected]> Cc: Josh Poimboeuf <[email protected]> Cc: Miroslav Benes <[email protected]> Cc: Petr Mladek <[email protected]> Cc: Song Liu <[email protected]> Cc: Will Deacon <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Catalin Marinas <[email protected]>
1 parent beecfd6 commit 805f13e

File tree

2 files changed

+44
-11
lines changed

2 files changed

+44
-11
lines changed

arch/arm64/Kconfig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,7 @@ config ARM64
279279
select HAVE_SOFTIRQ_ON_OWN_STACK
280280
select USER_STACKTRACE_SUPPORT
281281
select VDSO_GETRANDOM
282+
select HAVE_RELIABLE_STACKTRACE
282283
help
283284
ARM 64-bit (AArch64) Linux support.
284285

@@ -2498,4 +2499,3 @@ endmenu # "CPU Power Management"
24982499
source "drivers/acpi/Kconfig"
24992500

25002501
source "arch/arm64/kvm/Kconfig"
2501-

arch/arm64/kernel/stacktrace.c

Lines changed: 43 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -279,21 +279,24 @@ kunwind_next(struct kunwind_state *state)
279279

280280
typedef bool (*kunwind_consume_fn)(const struct kunwind_state *state, void *cookie);
281281

282-
static __always_inline void
282+
static __always_inline int
283283
do_kunwind(struct kunwind_state *state, kunwind_consume_fn consume_state,
284284
void *cookie)
285285
{
286-
if (kunwind_recover_return_address(state))
287-
return;
286+
int ret;
288287

289-
while (1) {
290-
int ret;
288+
ret = kunwind_recover_return_address(state);
289+
if (ret)
290+
return ret;
291291

292+
while (1) {
292293
if (!consume_state(state, cookie))
293-
break;
294+
return -EINVAL;
294295
ret = kunwind_next(state);
296+
if (ret == -ENOENT)
297+
return 0;
295298
if (ret < 0)
296-
break;
299+
return ret;
297300
}
298301
}
299302

@@ -326,7 +329,7 @@ do_kunwind(struct kunwind_state *state, kunwind_consume_fn consume_state,
326329
: stackinfo_get_unknown(); \
327330
})
328331

329-
static __always_inline void
332+
static __always_inline int
330333
kunwind_stack_walk(kunwind_consume_fn consume_state,
331334
void *cookie, struct task_struct *task,
332335
struct pt_regs *regs)
@@ -354,15 +357,15 @@ kunwind_stack_walk(kunwind_consume_fn consume_state,
354357

355358
if (regs) {
356359
if (task != current)
357-
return;
360+
return -EINVAL;
358361
kunwind_init_from_regs(&state, regs);
359362
} else if (task == current) {
360363
kunwind_init_from_caller(&state);
361364
} else {
362365
kunwind_init_from_task(&state, task);
363366
}
364367

365-
do_kunwind(&state, consume_state, cookie);
368+
return do_kunwind(&state, consume_state, cookie);
366369
}
367370

368371
struct kunwind_consume_entry_data {
@@ -389,6 +392,36 @@ noinline noinstr void arch_stack_walk(stack_trace_consume_fn consume_entry,
389392
kunwind_stack_walk(arch_kunwind_consume_entry, &data, task, regs);
390393
}
391394

395+
static __always_inline bool
396+
arch_reliable_kunwind_consume_entry(const struct kunwind_state *state, void *cookie)
397+
{
398+
/*
399+
* At an exception boundary we can reliably consume the saved PC. We do
400+
* not know whether the LR was live when the exception was taken, and
401+
* so we cannot perform the next unwind step reliably.
402+
*
403+
* All that matters is whether the *entire* unwind is reliable, so give
404+
* up as soon as we hit an exception boundary.
405+
*/
406+
if (state->source == KUNWIND_SOURCE_REGS_PC)
407+
return false;
408+
409+
return arch_kunwind_consume_entry(state, cookie);
410+
}
411+
412+
noinline noinstr int arch_stack_walk_reliable(stack_trace_consume_fn consume_entry,
413+
void *cookie,
414+
struct task_struct *task)
415+
{
416+
struct kunwind_consume_entry_data data = {
417+
.consume_entry = consume_entry,
418+
.cookie = cookie,
419+
};
420+
421+
return kunwind_stack_walk(arch_reliable_kunwind_consume_entry, &data,
422+
task, NULL);
423+
}
424+
392425
struct bpf_unwind_consume_entry_data {
393426
bool (*consume_entry)(void *cookie, u64 ip, u64 sp, u64 fp);
394427
void *cookie;

0 commit comments

Comments
 (0)