On Fri, 5 Sept 2025 at 02:24, Kees Cook <k...@kernel.org> wrote:
>
> Implement ARM 32-bit KCFI backend supporting ARMv7+:
>
> - Function preamble generation using .word directives for type ID storage
>   at -4 byte offset from function entry point (no prefix NOPs needed due to
>   4-byte instruction alignment).
>
> - Use movw/movt instructions for 32-bit immediate loading.
>
> - Trap debugging through UDF instruction immediate encoding following
>   AArch64 BRK pattern for encoding registers with useful contents.
>
> - Scratch register allocation using r0/r1 following ARM procedure call
>   standard for caller-saved temporary registers, though they get
>   stack spilled due to register pressure.
>
> Assembly Code Pattern for ARM 32-bit:
>   push {r0, r1}                ; Spill r0, r1
>   ldr  r0, [target, #-4]       ; Load actual type ID from preamble
>   movw r1, #type_id_low        ; Load expected type (lower 16 bits)
>   movt r1, #type_id_high       ; Load upper 16 bits with top instruction
>   cmp  r0, r1                  ; Compare type IDs directly
>   pop [r0, r1]                 ; Reload r0, r1

We could avoid the MOVW/MOVT pair and the spilling by doing something
along the lines of

ldr   ip, [target, #-4]
eor   ip, ip, #type_id[0]
eor   ip, ip, #type_id[1] << 8
eor   ip, ip, #type_id[2] << 16
eors  ip, ip, #type_id[3] << 24
ldrne ip, =type_id[3:0]

Note that IP (R12) should be dead before a function call. Here it is
conditionally loaded with the expected target typeid, removing the
need to decode the instructions to recover it when the trap occurs.

This should compile to Thumb2 as well as ARM encodings.


>   beq  .Lkcfi_call             ; Branch if typeids match
>   .Lkcfi_trap: udf #udf_value  ; Undefined instruction trap with encoding
>   .Lkcfi_call: blx/bx target   ; Execute validated indirect transfer
>
> UDF Immediate Encoding (following AArch64 ESR pattern):
> - UDF instruction immediate encoding format:
>   0x8000 | ((ExpectedTypeReg & 31) << 5) | (TargetAddrReg & 31)
>   - ExpectedTypeReg indicates which register contains expected type (R12 = 12)
>   - TargetAddrReg indicates which register contains target address (0-15)
>   - Example: udf #33154 (0x817A) = expected type in R12, target address in R2
>
> Build and run tested with Linux kernel ARCH=arm.
>
> gcc/ChangeLog:
>
>         config/arm/arm-protos.h: Declare KCFI helpers.
>         config/arm/arm.cc (arm_maybe_wrap_call_with_kcfi): New function.
>         (arm_maybe_wrap_call_value_with_kcfi): New function.
>         (arm_output_kcfi_insn): Emit KCFI assembly.
>         config/arm/arm.md: Add KCFI RTL patterns and hook expansion.
>         doc/invoke.texi: Document arm32 nuances.
>
> Signed-off-by: Kees Cook <k...@kernel.org>
> ---
>  gcc/config/arm/arm-protos.h |   4 +
>  gcc/config/arm/arm.cc       | 144 ++++++++++++++++++++++++++++++++++++
>  gcc/config/arm/arm.md       |  62 ++++++++++++++++
>  gcc/doc/invoke.texi         |  17 +++++
>  4 files changed, 227 insertions(+)
>

Reply via email to