Re: [PATCH 6/8] ftrace: Fix dead loop caused by direct call in ftrace selftest

From: Mark Rutland
Date: Thu Feb 02 2023 - 14:03:32 EST


On Wed, Feb 01, 2023 at 05:34:18PM +0100, Florent Revest wrote:
> From: Xu Kuohai <xukuohai@xxxxxxxxxx>
>
> After direct call is enabled for arm64, ftrace selftest enters a
> dead loop:
>
> <trace_selftest_dynamic_test_func>:
> 00 bti c
> 01 mov x9, x30 <trace_direct_tramp>:
> 02 bl <trace_direct_tramp> ----------> ret
> |
> lr/x30 is 03, return to 03
> |
> 03 mov w0, #0x0 <-----------------------------|
> | |
> | dead loop! |
> | |
> 04 ret ---- lr/x30 is still 03, go back to 03 ----|
>
> The reason is that when the direct caller trace_direct_tramp() returns
> to the patched function trace_selftest_dynamic_test_func(), lr is still
> the address after the instrumented instruction in the patched function,
> so when the patched function exits, it returns to itself!
>
> To fix this issue, we need to restore lr before trace_direct_tramp()
> exits, so use a dedicated trace_direct_tramp() for arm64.
>
> Reported-by: Li Huafei <lihuafei1@xxxxxxxxxx>
> Signed-off-by: Xu Kuohai <xukuohai@xxxxxxxxxx>
> Acked-by: Steven Rostedt (Google) <rostedt@xxxxxxxxxxx>
> Signed-off-by: Florent Revest <revest@xxxxxxxxxxxx>

Looking at this, I don't think that the existing trace_direct_tramp makes
sense -- in general a C function doesn't follow the direct call trampoline
calling convention, and cannot be used as a direct call trampoline.

Looking further, there's a distinct latent issue on s390, where the mismatch
between their regular calling convention and their direct call trampoline
calling convention means that trace_direct_tramp() returns into the *caller* of
the instrumented function, skipping that entirely (verified locally with QEMU
and printk()s added to DYN_FTRACE_TEST_NAME() / DYN_FTRACE_TEST_NAME2()
functions).

I think it'd be much better to do something like the below as a preparatory
cleanup (tested on s390 under QEMU).

Thanks,
Mark.

---->8----