Hi Josh, Ard,

On Sat, Aug 15, 2026 at 12:56:11PM +0300, Ard Biesheuvel wrote:
> On Sat, 15 Aug 2026, at 07:45, Josh Poimboeuf wrote:
> > The following BTI exception was seen when loading a livepatch module:
> >
> >   Internal error: Oops - BTI: 0000000036000001 [#1]  SMP
> >   pstate: 634004c9 (nZCv daIF +PAN -UAO +TCO +DIT -SSBS BTYPE=jc)
> >   pc : kill_orphaned_pgrp+0x0/0x150
> >   lr : do_exit+0x498/0xaf0 [livepatch_combined]
> >
> > The problem is that the patch module's do_exit() is branching to a
> > static function in vmlinux using a module PLT veneer (indirect branch),
> > but the target function doesn't have a BTI landing pad.
> >
> > Clang 21+ omits the landing pad for static functions which can only be
> > reached by a direct branch.  But livepatch modules use klp relocations
> > to reference arbitrary kernel symbols, and with
> > CONFIG_RANDOMIZE_MODULE_REGION_FULL the module is far enough away that
> > every call to vmlinux needs a PLT.
> >
> > Note this problem is actually not specific to livepatch.  It's possible
> > for any module's .init section to be allocated > 128MB away from its
> > .text section.  So calls from .init to .text via a PLT can trigger a BTI
> > exception when the target function doesn't have a landing pad.
> >
> > GCC has always omitted the landing pad when possible, so kernel BTI is
> > already considered incompatible with GCC since commit c0a454b9044f
> > ("arm64/bti: Disable in kernel BTI when cross section thunks are
> > broken").
> >
> > When missing landing pads are detected, allocate a page close to the
> > target which can be used to hold BTI veneers which receive PLT veneer
> > indirect branches and direct branch to the final target:
> >
> 
> This does not work for cross-section calls from .init.text to .text.
> 
> If .init.text is far away from .text, it is likely because .text
> ended up in the 128M 'near' module region, and .init.text did not.
> (They tend to end up in direct branching range of each otherwise.)
> 
> Given that the module init code is typically small, I don't think
> it is safe to assume that allocating a single page close enough to
> .text is going to be possible if allocating the space for .init.*
> was not.
> 
> IOW, the fix I proposed for cross-section calls is still needed
> with this approach.

Sorry to jump in here, but I couldn't figure out a better place to get
involved as we have a few threads on this now.

Overall, it seems to me like there are three cases we need to consider
for re-enabling BTI in the kernel:

1. A cross-section call that spans beyond the 128M range and therefore
   needs a veneer. I think the static linker should resolve this, probably
   by emitting a second veneer with the landing pad. Do we know if LLD
   gets this right?

2. Calls from modules to exported symbols that end up going via a PLT
   due to module placement. Exported symbols shouldn't be static, so we
   should have the correct BTI landing pad in this case (and if we don't,
   we should fix EXPORT_SYMBOL() to add it).

3. The livepatch case where a module appears to branch directly to
   static functions in the kernel. For this, I frankly think we should
   either make in-kernel BTI depend on !LIVEPATCH _or_ pass some compiler
   option (tbd) when livepatch is enabled so that we get landing pads
   for static functions (which is what I believe Clang/GCC used to do?).

What am I missing?

Will

Reply via email to