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