Re: [PATCH 0/8] unwind, arm64: add sframe unwinder for kernel

Weinan Liu
2025-02-06T15:02:14Z

> After some debugging this is what I found:
>
> devtmpfsd() calls devtmpfs_work_loop() which is marked '__noreturn' and has an
> infinite loop. The compiler puts the `bl` to devtmpfs_work_loop() as the the
> last instruction in devtmpfsd() and therefore on entry to devtmpfs_work_loop(),
> LR points to an instruction beyond devtmpfsd() and this consfuses the unwinder.
>
> ffff800080d9a070 <devtmpfsd>:
> ffff800080d9a070: d503201f nop
> ffff800080d9a074: d503201f nop
> ffff800080d9a078: d503233f paciasp
> ffff800080d9a07c: a9be7bfd stp x29, x30, [sp, #-32]!
> ffff800080d9a080: 910003fd mov x29, sp
> ffff800080d9a084: f9000bf3 str x19, [sp, #16]
> ffff800080d9a088: 943378e8 bl ffff800081a78428 <devtmpfs_setup>
> ffff800080d9a08c: 90006ca1 adrp x1, ffff800081b2e000 <unique_processor_ids+0x3758>
> ffff800080d9a090: 2a0003f3 mov w19, w0
> ffff800080d9a094: 912de021 add x1, x1, #0xb78
> ffff800080d9a098: 91002020 add x0, x1, #0x8
> ffff800080d9a09c: 97cd2a43 bl ffff8000800e49a8 <complete>
> ffff800080d9a0a0: 340000d3 cbz w19, ffff800080d9a0b8 <devtmpfsd+0x48>
> ffff800080d9a0a4: 2a1303e0 mov w0, w19
> ffff800080d9a0a8: f9400bf3 ldr x19, [sp, #16]
> ffff800080d9a0ac: a8c27bfd ldp x29, x30, [sp], #32
> ffff800080d9a0b0: d50323bf autiasp
> ffff800080d9a0b4: d65f03c0 ret
> ffff800080d9a0b8: 97f06526 bl ffff8000809b3550 <devtmpfs_work_loop>
> ffff800080d9a0bc: 00000000 udf #0
> ffff800080d9a0c0: d503201f nop
> ffff800080d9a0c4: d503201f nop
>
> find_fde() got pc=0xffff800080d9a0bc which is not in [sfde_func_start_address, sfde_func_size)
>
> output for readelf --sframe for devtmpfsd()
>
> func idx [51825]: pc = 0xffff800080d9a070, size = 76 bytes
> STARTPC CFA FP RA
> ffff800080d9a070 sp+0 u u
> ffff800080d9a07c sp+0 u u[s]
> ffff800080d9a080 sp+32 c-32 c-24[s]
> ffff800080d9a0b0 sp+0 u u[s]
> ffff800080d9a0b4 sp+0 u u
> ffff800080d9a0b8 sp+32 c-32 c-24[s]
>
> The unwinder and all the related infra is assuming that the return address
> will be part of a valid function which is not the case here.
>
> I am not sure which component needs to be fixed here, but the following
> patch(which is a hack) fixes the issue by considering the return address as
> part of the function descriptor entry.
>
> -- 8< --
>
> diff --git a/kernel/sframe_lookup.c b/kernel/sframe_lookup.c
> index 846f1da95..28bec5064 100644
> --- a/kernel/sframe_lookup.c
> +++ b/kernel/sframe_lookup.c
> @@ -82,7 +82,7 @@ static struct sframe_fde *find_fde(const struct sframe_table *tbl, unsigned long
> if (f >= tbl->sfhdr_p->num_fdes || f < 0)
> return NULL;
> fdep = tbl->fde_p + f;
> - if (ip < fdep->start_addr || ip >= fdep->start_addr + fdep->size)
> + if (ip < fdep->start_addr || ip > fdep->start_addr + fdep->size)
> return NULL;
>
> return fdep;
> @@ -106,7 +106,7 @@ static int find_fre(const struct sframe_table *tbl, unsigned long pc,
> else
> ip_off = (int32_t)(pc - (unsigned long)tbl->sfhdr_p) - fdep->start_addr;
>
> - if (ip_off < 0 || ip_off >= fdep->size)
> + if (ip_off < 0 || ip_off > fdep->size)
> return -EINVAL;
>
> /*
>
> -- >8 --
>
> Thanks,
> Puranjay

Thank you for reporting this issue.
I just found out that Josh also intentionally uses '>' instead of '>=' for the same reason
https://lore.kernel.org/lkml/20250122225257.h64ftfnorofe7cb4@jpoimboe/T/#m6d70a20ed9f5b3bbe5b24b24b8c5dcc603a79101

QQ, do we need to care the stacktrace after '__noreturn' function?