On Sun, 16 Aug 2026, at 12:41, Will Deacon wrote:
> 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?
>
There are two variants here:
A cross-section call .init.text to .text that spans beyond the 128M range:
1a. inside vmlinux, which should be dealt with by the linker, but which might
be unreliable in practice due to the lack of BTI annotations in asm files,
missing exec permissions on ELF sections etc. This is addressed by this series,
but is only an issue for unusually large kernel images (e.g., allyesconfig).
1b. inside a module, which the toolchains's static linker cannot be expected to
deal with, given that modules are not fully linked executables. Therefore, the
module loader should deal with this, which is what my patch [0] implements. Note
that this only occurs when .init.text ends up far away from the .text section of
the same module, which occurs rarely in practice.
> 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).
>
This is not an issue today as far as I am aware.
> 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?).
>
The livepatch practice of lifting code out of vmlinux at function
granularity and copying it into KLP modules in order to patch a running
kernel is so far removed from the typical usage expected by the toolchain
that it is justified to treat it as a separate case. Working with the
compiler folks to allow this optimization to be disabled would be one way
to deal with this (and disabling kernel mode BTI for compilers that do
not implement it)
That said, my patch [0] only fixes case 1b., which is quite rare in
practice, and could perhaps be eliminated entirely if we manage to tweak
the module loader so that it always allocates .text and .init.text from
the same region. (I haven't checked whether this is feasible at all tbh)
If we decide that the KLP case is worth fixing too, and tweaking the module
loader is impractical, I think we should rely on the same logic for case 1b
(I mistakenly thought that it doesn't work for that case but Josh pointed
out that the 256M allocation window around the branch target should be
large enough to ensure that a veneer can be allocated within direct
branching range)
Whatever we end up doing, we might still consider my approach as a temp
solution that can be backported if desired.
[0]
https://lore.kernel.org/linux-arm-kernel/[email protected]/