On Thu, Jul 10, 2025 at 06:35:13PM +0200, Jens Remus wrote:
> +++ b/arch/Kconfig
> @@ -450,6 +450,11 @@ config HAVE_UNWIND_USER_SFRAME
>       bool
>       select UNWIND_USER
>  
> +config HAVE_USER_RA_REG
> +     bool
> +     help
> +       The arch passes the return address (RA) in user space in a register.

How about "HAVE_UNWIND_USER_RA_REG" so it matches the existing
namespace?

> @@ -310,6 +307,12 @@ static __always_inline int __find_fre(struct 
> sframe_section *sec,
>               return -EINVAL;
>       fre = prev_fre;
>  
> +     if ((!IS_ENABLED(CONFIG_HAVE_USER_RA_REG) || !topmost) && !fre->ra_off) 
> {
> +             dbg_sec_uaccess("fde addr 0x%x: zero ra_off\n",
> +                             fde->start_addr);
> +             return -EINVAL;
> +     }

The topmost frame doesn't necessarily (or even likely) come from before
the prologue, or from a leaf function, so this check would miss the case
where a non-leaf function wrongly has !ra_off after its prologue.

Which in reality is probably fine, as there are other guardrails in
place to catch such bad sframe data.

But then do we think the !ra_off check is still worth the effort?  It
would be simpler to just always assume !ra_off is valid for the
CONFIG_HAVE_USER_RA_REG case.

I think I prefer the simplicity of removing the check, as the error
would be rare, and corrupt sframe would be caught in other ways.

> @@ -86,18 +88,28 @@ static int unwind_user_next(struct unwind_user_state 
> *state)
>  
>       /* Get the Stack Pointer (SP) */
>       sp = cfa + frame->sp_val_off;
> -     /* Make sure that stack is not going in wrong direction */
> -     if (sp <= state->sp)
> +     /*
> +      * Make sure that stack is not going in wrong direction.  Allow SP
> +      * to be unchanged for the topmost frame, by subtracting topmost,
> +      * which is either 0 or 1.
> +      */
> +     if (sp <= state->sp - topmost)
>               goto done;
>  
> -     /* Make sure that the address is word aligned */
> -     shift = sizeof(long) == 4 || compat_fp_state(state) ? 2 : 3;
> -     if ((cfa + frame->ra_off) & ((1 << shift) - 1))
> -             goto done;
>  
>       /* Get the Return Address (RA) */
> -     if (unwind_get_user_long(ra, cfa + frame->ra_off, state))
> -             goto done;
> +     if (frame->ra_off) {
> +             /* Make sure that the address is word aligned */
> +             shift = sizeof(long) == 4 || compat_fp_state(state) ? 2 : 3;
> +             if ((cfa + frame->ra_off) & ((1 << shift) - 1))
> +                     goto done;
> +             if (unwind_get_user_long(ra, cfa + frame->ra_off, state))
> +                     goto done;
> +     } else {
> +             if (!IS_ENABLED(CONFIG_HAVE_USER_RA_REG) || !topmost)
> +                     goto done;

I think this check is redundant with the one in __find_fre()?

-- 
Josh

Reply via email to