Re: [PATCH v3 8/8] unwind: arm64: Use sframe to unwind interrupt frames.
Dylan Hatch <[email protected]> Sun, 19 Apr 2026 22:56:25 -0700
| Newsgroups | org.kernel.vger.linux-toolchains,org.infradead.lists.linux-arm-kernel,org.kernel.vger.linux-kernel,org.kernel.vger.live-patching |
|---|---|
| Message-ID | <CADBMgpwjDf44p0ApR1=XVStCyN-0Q6tuywJ4ixLcbaLZOSjjBg@mail.gmail.com> |
On Fri, Apr 17, 2026 at 8:45=E2=80=AFAM Jens Remus <[email protected]> w= rote: > > > + case UNWIND_CFA_RULE_FP_OFFSET: > > + if (state->common.fp < state->common.sp) > > + return -EINVAL; > > I wonder whether that check is valid in kernel? Looking at > call_on_irq_stack() saving SP in FP and then loading SP with the IRQ SP. > Is that condition always true then? Good catch. I will double-check this. > > > + cfa =3D state->common.fp; > > + break; > > + case UNWIND_CFA_RULE_REG_OFFSET: > > + case UNWIND_CFA_RULE_REG_OFFSET_DEREF: > > + if (!regs) > > if (!regs || frame.cfa.regnum > 30) > > > + return -EINVAL; > > + cfa =3D regs->regs[frame.cfa.regnum]; > > In unwind user this is guarded by a topmost frame check, as arbitrary > registers are otherwise not available. Isn't this necessary in the > kernel case? It is necessary, though as you point out the way I wrote the check is not as obvious as it probably should be. The saved state->regs is set when the current frame is recovered from the saved PC of a struct pt_regs, and then immediately set back to NULL after the next frame has been recovered. In other words, the state->regs is only ever set when it is relevant to the current frame, which occurs when state->source =3D=3D KUNWIND_SOURCE_REGS_PC. This only happens when the topmost frame is recovered from a pt_regs, or when a pt_regs is recovered from the stack due to an interrupt. I can make this more readable by adding an explicit check for KUNWIND_SOURCE_REGS_PC in addition to state->regs !=3D NULL. > > > + break; > > + default: > > + WARN_ON_ONCE(1); > > + return -EINVAL; > > + } > > + cfa +=3D frame.cfa.offset; > > + > > + /* > > + * CFA typically points to a higher address than RA or FP, so don= 't > > + * consume from the stack when we read it. > > + */ > > + if (frame.cfa.rule & UNWIND_RULE_DEREF && > > + !get_word(&state->common, &cfa)) > > + return -EINVAL; > > + > > + /* CFA alignment 8 bytes */ > > + if (cfa & 0x7) > > + return -EINVAL; > > + > > + /* Get the Return Address (RA) */ > > + switch (frame.ra.rule) { > > + case UNWIND_RULE_RETAIN: > > + if (!regs) > > + return -EINVAL; > > + ra =3D regs->regs[30]; > > Likewise: Topmost frame check not required to access arbitrary registers > (including RA/LR)? Furthermore, provided don't have a thinko, LR may > only be in LR in the topmost frame. In any other frame it must have > been saved. Otherwise there would be an endless return loop. > > > + source =3D KUNWIND_SOURCE_REGS_LR; > > + break; > > + /* UNWIND_USER_RULE_CFA_OFFSET not implemented on purpose */ > > + case UNWIND_RULE_CFA_OFFSET_DEREF: > > + ra =3D cfa + frame.ra.offset; > > + break; > > + case UNWIND_RULE_REG_OFFSET: > > + case UNWIND_RULE_REG_OFFSET_DEREF: > > + if (!regs) > > if (!regs || frame.cfa.regnum > 30) > > > + return -EINVAL; > > + ra =3D regs->regs[frame.cfa.regnum]; > > Likewise: Topmost frame check not required to access arbitrary registers? > > > + ra +=3D frame.ra.offset; > > + break; > > + default: > > + WARN_ON_ONCE(1); > > + return -EINVAL; > > + } > > + > > + /* Get the Frame Pointer (FP) */ > > + switch (frame.fp.rule) { > > + case UNWIND_RULE_RETAIN: > > + fp =3D state->common.fp; > > + break; > > + /* UNWIND_USER_RULE_CFA_OFFSET not implemented on purpose */ > > + case UNWIND_RULE_CFA_OFFSET_DEREF: > > + fp =3D cfa + frame.fp.offset; > > + break; > > + case UNWIND_RULE_REG_OFFSET: > > + case UNWIND_RULE_REG_OFFSET_DEREF: > > + if (!regs) > > if (!regs || frame.cfa.regnum > 30) > > > + return -EINVAL; > > + fp =3D regs->regs[frame.fp.regnum]; > > Likewise: Topmost frame check not required to access arbitrary registers? > > > + fp +=3D frame.fp.offset; > > + break; > > + default: > > + WARN_ON_ONCE(1); > > + return -EINVAL; > > + } > > + > > + /* > > + * Consume RA and FP from the stack. The frame record puts FP at = a lower > > + * address than RA, so we always read FP first. > > + */ > > + if (frame.fp.rule & UNWIND_RULE_DEREF && > > + !get_word(&state->common, &fp)) > > + return -EINVAL; > > + > > + if (frame.ra.rule & UNWIND_RULE_DEREF && > > + get_consume_word(&state->common, &ra)) > > + return -EINVAL; > > + > > + state->common.pc =3D ra; > > + state->common.sp =3D cfa; > > + state->common.fp =3D fp; > > + > > + state->source =3D source; > > + > > + return 0; > > +} > Thanks and regards, > Jens > -- > Jens Remus > Linux on Z Development (D3303) > [email protected] / [email protected] > > IBM Deutschland Research & Development GmbH; Vorsitzender des Aufsichtsra= ts: Wolfgang Wendt; Gesch=C3=A4ftsf=C3=BChrung: David Faller; Sitz der Gese= llschaft: Ehningen; Registergericht: Amtsgericht Stuttgart, HRB 243294 > IBM Data Privacy Statement: https://www.ibm.com/privacy/ > Thanks, Dylan