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

Commit b5e7307d authored by Mark Rutland's avatar Mark Rutland Committed by Will Deacon
Browse files

arm64: fix dump_backtrace/unwind_frame with NULL tsk



In some places, dump_backtrace() is called with a NULL tsk parameter,
e.g. in bug_handler() in arch/arm64, or indirectly via show_stack() in
core code. The expectation is that this is treated as if current were
passed instead of NULL. Similar is true of unwind_frame().

Commit a80a0eb7 ("arm64: make irq_stack_ptr more robust") didn't
take this into account. In dump_backtrace() it compares tsk against
current *before* we check if tsk is NULL, and in unwind_frame() we never
set tsk if it is NULL.

Due to this, we won't initialise irq_stack_ptr in either function. In
dump_backtrace() this results in calling dump_mem() for memory
immediately above the IRQ stack range, rather than for the relevant
range on the task stack. In unwind_frame we'll reject unwinding frames
on the IRQ stack.

In either case this results in incomplete or misleading backtrace
information, but is not otherwise problematic. The initial percpu areas
(including the IRQ stacks) are allocated in the linear map, and dump_mem
uses __get_user(), so we shouldn't access anything with side-effects,
and will handle holes safely.

This patch fixes the issue by having both functions handle the NULL tsk
case before doing anything else with tsk.

Signed-off-by: default avatarMark Rutland <mark.rutland@arm.com>
Fixes: a80a0eb7 ("arm64: make irq_stack_ptr more robust")
Acked-by: default avatarJames Morse <james.morse@arm.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Cc: Yang Shi <yang.shi@linaro.org>
Signed-off-by: default avatarWill Deacon <will.deacon@arm.com>
parent 1d8f51d4
Loading
Loading
Loading
Loading
+4 −1
Original line number Diff line number Diff line
@@ -43,6 +43,9 @@ int notrace unwind_frame(struct task_struct *tsk, struct stackframe *frame)
	unsigned long fp = frame->fp;
	unsigned long irq_stack_ptr;

	if (!tsk)
		tsk = current;

	/*
	 * Switching between stacks is valid when tracing current and in
	 * non-preemptible context.
@@ -67,7 +70,7 @@ int notrace unwind_frame(struct task_struct *tsk, struct stackframe *frame)
	frame->pc = READ_ONCE_NOCHECK(*(unsigned long *)(fp + 8));

#ifdef CONFIG_FUNCTION_GRAPH_TRACER
	if (tsk && tsk->ret_stack &&
	if (tsk->ret_stack &&
			(frame->pc == (unsigned long)return_to_handler)) {
		/*
		 * This is a case where function graph tracer has
+5 −5
Original line number Diff line number Diff line
@@ -142,6 +142,11 @@ static void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk)
	unsigned long irq_stack_ptr;
	int skip;

	pr_debug("%s(regs = %p tsk = %p)\n", __func__, regs, tsk);

	if (!tsk)
		tsk = current;

	/*
	 * Switching between stacks is valid when tracing current and in
	 * non-preemptible context.
@@ -151,11 +156,6 @@ static void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk)
	else
		irq_stack_ptr = 0;

	pr_debug("%s(regs = %p tsk = %p)\n", __func__, regs, tsk);

	if (!tsk)
		tsk = current;

	if (tsk == current) {
		frame.fp = (unsigned long)__builtin_frame_address(0);
		frame.sp = current_stack_pointer;