BPF List
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Jens Remus" <jremus@linux.ibm.com>
Cc: bpf@vger.kernel.org
Subject: Re: [PATCH v14 16/19] unwind_user/sframe: Add support for SFrame V3 flexible FDEs
Date: Tue, 05 May 2026 18:55:02 +0000	[thread overview]
Message-ID: <20260505185502.CB501C2BCB4@smtp.kernel.org> (raw)
In-Reply-To: <20260505121718.3572346-17-jremus@linux.ibm.com>

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] Valid flexible FREs ending in a single padding data word are incorrectly rejected.
- [High] Valid flexible FRE rules for CFA and FP/RA produce unhandled enums, triggering user-controlled WARN_ON_ONCE().
- [High] Outermost frame unwinding is unconditionally broken, causing unintended fallback to frame pointers.
--

commit 7d3289b2e756d08c38877458400ce276ff3f5994
Author: Jens Remus <jremus@linux.ibm.com>

unwind_user/sframe: Add support for SFrame V3 flexible FDEs

SFrame V3 introduces flexible FDEs in addition to the regular FDEs.
The key difference is that flexible FDEs encode the CFA, RA, and FP
tracking information using two FRE data words, a control word and an
offset, or a single padding data word of zero.

> diff --git a/kernel/unwind/sframe.c b/kernel/unwind/sframe.c
> index 2721f4af53fd3..2ba2c8b385f96 100644
> --- a/kernel/unwind/sframe.c
> +++ b/kernel/unwind/sframe.c
[ ... ]
> @@ -189,16 +193,147 @@ static __always_inline int __find_fde(struct sframe_section *sec,
[ ... ]
> +static __always_inline int
> +__read_flex_fde_fre_datawords(struct sframe_section *sec,
> +			      struct sframe_fde_internal *fde,
> +			      unsigned long cur,
> +			      unsigned char dataword_count,
> +			      unsigned char dataword_size,
> +			      struct sframe_fre_internal *fre)
> +{
> +	u32 cfa_ctl, ra_ctl, fp_ctl;
> +	s32 cfa_off, ra_off, fp_off;
> +
> +	if (dataword_count < 2)
> +		return -EFAULT;
> +	UNSAFE_GET_USER_INC(cfa_ctl, cur, dataword_size, Efault);
> +	UNSAFE_GET_USER_INC(cfa_off, cur, dataword_size, Efault);
> +	dataword_count -= 2;
> +
> +	ra_off = sec->ra_off;
> +	ra_ctl = ra_off ? 2 : 0; /* regnum=0, deref_p=(ra_off != 0), reg_p=0 */
> +	if (dataword_count >= 2) {

The commit message mentions that flexible FDEs can use a single padding
data word of zero. However, this code checks dataword_count >= 2 for both
the RA and FP blocks.

If an FRE ends with a single padding data word, it will bypass both of these
blocks and reach the check below:

> +		UNSAFE_GET_USER_INC(ra_ctl, cur, dataword_size, Efault);
> +		dataword_count--;
> +		if (ra_ctl) {
> +			UNSAFE_GET_USER_INC(ra_off, cur, dataword_size, Efault);
> +			dataword_count--;
> +		} else {
> +			/* Padding RA location info */
> +			ra_ctl = ra_off ? 2 : 0; /* re-deduce (see above) */
> +		}
> +	}
> +
> +	fp_off = sec->fp_off;
> +	fp_ctl = fp_off ? 2 : 0; /* regnum=0, deref_p=(fp_off != 0), reg_p=0 */
> +	if (dataword_count >= 2) {
> +		UNSAFE_GET_USER_INC(fp_ctl, cur, dataword_size, Efault);
> +		dataword_count--;
> +		if (fp_ctl) {
> +			UNSAFE_GET_USER_INC(fp_off, cur, dataword_size, Efault);
> +			dataword_count--;
> +		} else {
> +			/* Padding FP location info */
> +			fp_ctl = fp_off ? 2 : 0; /* re-deduce (see above) */
> +		}
> +	}
> +
> +	if (dataword_count)
> +		return -EFAULT;

Could this incorrectly reject valid FREs ending in a single padding data word
since dataword_count would be 1 here?

> +
> +	fre->cfa_ctl	= cfa_ctl;
> +	fre->cfa_off	= cfa_off;
> +	fre->ra_ctl	= ra_ctl;
> +	fre->ra_off	= ra_off;
> +	fre->fp_ctl	= fp_ctl;
> +	fre->fp_off	= fp_off;
> +
> +	return 0;
> +
> +Efault:
> +	return -EFAULT;
> +}
[ ... ]
>  static __always_inline int __read_fre(struct sframe_section *sec,
>  				      struct sframe_fde_internal *fde,
>  				      unsigned long fre_addr,
>  				      struct sframe_fre_internal *fre)
>  {
[ ... ]
>  	if (!dataword_count) {
>  		/*
>  		 * A FRE without data words indicates RA undefined /
>  		 * outermost frame.
>  		 */
> -		cfa_off	= 0;
> -		ra_off	= 0;
> -		fp_off	= 0;
> -		goto done;
> -	}
> +		fre->cfa_ctl	= 0;
> +		fre->cfa_off	= 0;
> +		fre->ra_ctl	= 0;
> +		fre->ra_off	= 0;
> +		fre->fp_ctl	= 0;
> +		fre->fp_off	= 0;
>  
> -	UNSAFE_GET_USER_INC(cfa_off, cur, dataword_size, Efault);

When __read_fre() processes an outermost frame, it zeroes out fre->cfa_ctl.
In __find_fre(), this is later passed to sframe_init_cfa_rule_data():

> -static __always_inline void
> +static __always_inline int
>  sframe_init_cfa_rule_data(struct unwind_user_cfa_rule_data *cfa_rule_data,
> -			  unsigned char fre_info,
> -			  s32 offset)
> +			  u32 ctlword, s32 offset)
>  {
> -	if (SFRAME_V3_FRE_CFA_BASE_REG_ID(fre_info) == SFRAME_BASE_REG_FP)
> -		cfa_rule_data->rule = UNWIND_USER_CFA_RULE_FP_OFFSET;
> -	else
> -		cfa_rule_data->rule = UNWIND_USER_CFA_RULE_SP_OFFSET;
> +	bool deref_p = SFRAME_V3_FLEX_FDE_CTRLWORD_DEREF_P(ctlword);
> +	bool reg_p = SFRAME_V3_FLEX_FDE_CTRLWORD_REG_P(ctlword);
> +
> +	if (reg_p) {
> +		unsigned int regnum = SFRAME_V3_FLEX_FDE_CTRLWORD_REGNUM(ctlword);
> +
> +		switch (regnum) {
> +		case SFRAME_REG_SP:
> +			cfa_rule_data->rule = UNWIND_USER_CFA_RULE_SP_OFFSET;
> +			break;
> +		case SFRAME_REG_FP:
> +			cfa_rule_data->rule = UNWIND_USER_CFA_RULE_FP_OFFSET;
> +			break;
> +		default:
> +			cfa_rule_data->rule = UNWIND_USER_CFA_RULE_REG_OFFSET;
> +			cfa_rule_data->regnum = regnum;
> +		}
> +	} else {
> +		return -EINVAL;
> +	}

Because ctlword is 0 for an outermost frame, reg_p will evaluate to false,
and this unconditionally returns -EINVAL.

Does this unintentionally break outermost frame unwinding? Returning an
error here will cause __find_fre() to fail before it can reach the
frame->outermost check, resulting in a fallback to frame pointer unwinding
instead of stopping gracefully.

> +
> +	if (deref_p)
> +		cfa_rule_data->rule |= UNWIND_USER_RULE_DEREF;
> +

Also, does this append UNWIND_USER_RULE_DEREF to CFA rules in a way that is
not supported by the unwinder?

If a CFA rule uses SP/FP with deref_p=1, this creates a combined enum like
UNWIND_USER_CFA_RULE_SP_OFFSET | UNWIND_USER_RULE_DEREF. Because .sframe
sections are user-supplied, could an attacker use this to trigger an
unhandled enum path and hit a WARN_ON_ONCE() during unwinding?

>  	cfa_rule_data->offset = offset;
> +
> +	return 0;
>  }
>  
>  static __always_inline void
>  sframe_init_rule_data(struct unwind_user_rule_data *rule_data,
> -		      s32 offset)
> +		      u32 ctlword, s32 offset)
>  {
> -	if (offset) {
> -		rule_data->rule = UNWIND_USER_RULE_CFA_OFFSET_DEREF;
> -		rule_data->offset = offset;
> -	} else {
> +	bool deref_p = SFRAME_V3_FLEX_FDE_CTRLWORD_DEREF_P(ctlword);
> +	bool reg_p = SFRAME_V3_FLEX_FDE_CTRLWORD_REG_P(ctlword);
> +
> +	if (!ctlword && !offset) {
>  		rule_data->rule = UNWIND_USER_RULE_RETAIN;
> +		return;
> +	}
> +	if (reg_p) {
> +		unsigned int regnum = SFRAME_V3_FLEX_FDE_CTRLWORD_REGNUM(ctlword);
> +
> +		rule_data->rule = UNWIND_USER_RULE_REG_OFFSET;
> +		rule_data->regnum = regnum;
> +	} else {
> +		rule_data->rule = UNWIND_USER_RULE_CFA_OFFSET;
>  	}
> +
> +	if (deref_p)
> +		rule_data->rule |= UNWIND_USER_RULE_DEREF;
> +
> +	rule_data->offset = offset;
>  }

Similarly, if a rule for FP/RA is set to *(CFA + offset) with reg_p=0
and deref_p=0, this assigns UNWIND_USER_RULE_CFA_OFFSET without the
UNWIND_USER_RULE_DEREF flag.

Is this combination handled by the rest of the unwinding logic, or will
it fall through to an unsupported state and abort unwinding?

>  
>  static __always_inline int __find_fre(struct sframe_section *sec,
> @@ -344,9 +492,10 @@ static __always_inline int __find_fre(struct sframe_section *sec,
>  		return -EINVAL;
>  	fre = prev_fre;
>  
> -	sframe_init_cfa_rule_data(&frame->cfa, fre->info, fre->cfa_off);
> -	sframe_init_rule_data(&frame->ra, fre->ra_off);
> -	sframe_init_rule_data(&frame->fp, fre->fp_off);
> +	if (sframe_init_cfa_rule_data(&frame->cfa, fre->cfa_ctl, fre->cfa_off))
> +		return -EINVAL;
> +	sframe_init_rule_data(&frame->ra, fre->ra_ctl, fre->ra_off);
> +	sframe_init_rule_data(&frame->fp, fre->fp_ctl, fre->fp_off);
>  	frame->outermost = SFRAME_V3_FRE_RA_UNDEFINED_P(fre->info);
>  
>  	return 0;

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260505121718.3572346-1-jremus@linux.ibm.com?part=16

  reply	other threads:[~2026-05-05 18:55 UTC|newest]

Thread overview: 80+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-05-05 12:16 [PATCH v14 00/19] unwind_deferred: Implement sframe handling Jens Remus
2026-05-05 12:17 ` [PATCH v14 01/19] unwind_user: Add generic and arch-specific headers to MAINTAINERS Jens Remus
2026-05-05 12:17 ` [PATCH v14 02/19] unwind_user/sframe: Add support for reading .sframe headers Jens Remus
2026-05-05 12:49   ` sashiko-bot
2026-05-06 13:42     ` Jens Remus
2026-05-07 14:55       ` Jens Remus
2026-05-08 23:02       ` Indu Bhagat
2026-05-11 10:05         ` Jens Remus
2026-05-05 12:17 ` [PATCH v14 03/19] unwind_user/sframe: Store .sframe section data in per-mm maple tree Jens Remus
2026-05-05 18:51   ` sashiko-bot
2026-05-06 13:50     ` Jens Remus
2026-05-06 15:21       ` Steven Rostedt
2026-05-12 15:52         ` Jens Remus
2026-05-05 12:17 ` [PATCH v14 04/19] x86/uaccess: Add unsafe_copy_from_user() implementation Jens Remus
2026-05-05 18:22   ` sashiko-bot
2026-05-06 14:13     ` Jens Remus
2026-05-06 15:05       ` Steven Rostedt
2026-05-06 14:09   ` Jens Remus
2026-05-06 15:03     ` Steven Rostedt
2026-05-06 21:13     ` David Laight
2026-05-06 21:17       ` David Laight
2026-05-05 12:17 ` [PATCH v14 05/19] unwind_user/sframe: Add support for reading .sframe contents Jens Remus
2026-05-05 18:59   ` sashiko-bot
2026-05-06 14:34     ` Jens Remus
2026-05-06 15:01       ` Steven Rostedt
2026-05-06 15:29         ` Jens Remus
2026-05-08  9:49         ` Jens Remus
2026-05-08 23:04           ` Indu Bhagat
2026-05-12 13:35         ` Jens Remus
2026-05-13 12:22           ` Steven Rostedt
2026-05-08 23:03       ` Indu Bhagat
2026-05-08 10:50   ` Jens Remus
2026-05-11 16:16   ` Jens Remus
2026-05-05 12:17 ` [PATCH v14 06/19] unwind_user/sframe: Detect .sframe sections in executables Jens Remus
2026-05-05 12:53   ` sashiko-bot
2026-05-06 14:56     ` Jens Remus
2026-05-06 15:36       ` Steven Rostedt
2026-05-08 23:05         ` Indu Bhagat
2026-05-05 12:17 ` [PATCH v14 07/19] unwind_user/sframe: Wire up unwind_user to sframe Jens Remus
2026-05-05 18:55   ` sashiko-bot
2026-05-07 16:18     ` Jens Remus
2026-05-08 23:07       ` Indu Bhagat
2026-05-11 16:46         ` Steven Rostedt
2026-05-05 12:17 ` [PATCH v14 08/19] unwind_user: Stop when reaching an outermost frame Jens Remus
2026-05-05 12:40   ` sashiko-bot
2026-05-06 15:01     ` Jens Remus
2026-05-06 15:40       ` Steven Rostedt
2026-05-05 12:17 ` [PATCH v14 09/19] unwind_user/sframe: Add support for outermost frame indication Jens Remus
2026-05-05 12:17 ` [PATCH v14 10/19] unwind_user/sframe: Remove .sframe section on detected corruption Jens Remus
2026-05-05 20:39   ` sashiko-bot
2026-05-07 16:23     ` Jens Remus
2026-05-05 12:17 ` [PATCH v14 11/19] unwind_user/sframe: Show file name in debug output Jens Remus
2026-05-05 18:46   ` sashiko-bot
2026-05-12 14:52     ` Jens Remus
2026-05-13  9:20       ` Jens Remus
2026-05-05 12:17 ` [PATCH v14 12/19] unwind_user/sframe: Add .sframe validation option Jens Remus
2026-05-05 18:32   ` sashiko-bot
2026-05-12 14:23     ` Jens Remus
2026-05-13 12:30       ` Steven Rostedt
2026-05-08 10:51   ` Jens Remus
2026-05-05 12:17 ` [PATCH v14 13/19] unwind_user: Enable archs that pass RA in a register Jens Remus
2026-05-05 18:35   ` sashiko-bot
2026-05-05 12:17 ` [PATCH v14 14/19] unwind_user: Flexible FP/RA recovery rules Jens Remus
2026-05-05 18:34   ` sashiko-bot
2026-05-05 12:17 ` [PATCH v14 15/19] unwind_user: Flexible CFA " Jens Remus
2026-05-05 12:17 ` [PATCH v14 16/19] unwind_user/sframe: Add support for SFrame V3 flexible FDEs Jens Remus
2026-05-05 18:55   ` sashiko-bot [this message]
2026-05-07 15:30     ` Jens Remus
2026-05-13  6:26       ` Indu Bhagat
2026-05-13 13:50         ` Jens Remus
2026-05-13 15:16           ` Steven Rostedt
2026-05-05 12:17 ` [PATCH v14 17/19] unwind_user/sframe: Separate reading of FRE from reading of FRE data words Jens Remus
2026-05-05 19:05   ` sashiko-bot
2026-05-07 16:01     ` Jens Remus
2026-05-05 12:17 ` [PATCH v14 18/19] unwind_user/sframe/x86: Enable sframe unwinding on x86 Jens Remus
2026-05-05 19:07   ` sashiko-bot
2026-05-05 12:17 ` [PATCH v14 19/19] unwind_user/sframe: Add prctl() interface for registering .sframe sections Jens Remus
2026-05-05 18:45   ` sashiko-bot
2026-05-07 14:14     ` Jens Remus
2026-05-05 12:25 ` [PATCH v14 00/19] unwind_deferred: Implement sframe handling Jens Remus

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260505185502.CB501C2BCB4@smtp.kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=bpf@vger.kernel.org \
    --cc=jremus@linux.ibm.com \
    --cc=sashiko@lists.linux.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox