On Wed, Aug 26, 2026 at 1:59 AM Kees Cook <[email protected]> wrote:
>
> Implement x86_64-specific KCFI backend:
>
> - Implies -mindirect-branch-register since KCFI needs call target in
> a register for typeid hash loading.
>
> - Function preamble generation with type IDs positioned at -(4+prefix_nops)
> offset from function entry point.
>
> - Function-aligned KCFI preambles using calculated alignment padding NOPs:
> aligned(prefix_nops + 5, $func_align) to maintain ability to call the
> __cfi_ preamble directly in the case of Linux's FineIBT alternative
> CFI sequences (live patched into place).
>
> - Type-id hash avoids generating ENDBR instruction in type IDs
> (0xfa1e0ff3/0xfb1e0ff3 are incremented by 1 to prevent execution).
>
> - On-demand scratch register allocation strategy (r11 as needed), with
> the clobbers added when KCFI is used.
>
> - Incompatible with -ffixed-r10 or -ffixed-r11.
>
> - Uses the .kcfi_traps section for debugger/runtime metadata.
>
> - Introduces -fsanitize-kcfi-arity to enable function arg count to be
> represented in the kcfi hash preamble for FineIBT.
>
> Assembly Code Pattern layout required by Linux kernel:
> movl $inverse_type_id, %r10d ; Load expected type (0 - hash)
> addl offset(%target), %r10d ; Add stored type ID from preamble
> je .Lkcfi_call ; Branch if types match (sum == 0)
> .Lkcfi_trap: ud2 ; Undefined instruction trap on mismatch
> .Lkcfi_call: call/jmp *%target ; Execute validated indirect transfer
>
> Build and run tested on x86_64 Linux kernel with various CPU errata
> handling alternatives, with and without FineIBT patching.
>
> gcc/ChangeLog:
>
> * common.opt (fsanitize-kcfi-arity): New option.
> * config/i386/i386.h: KCFI enables TARGET_INDIRECT_BRANCH_REGISTER.
> * config/i386/i386-protos.h: Update ix86_output_call_insn.
> * config/i386/i386-expand.cc (ix86_expand_call): Expand indirect
> calls into KCFI RTL.
> * config/i386/i386.cc (ix86_kcfi_mask_type_id): New function.
> (ix86_output_call_insn): Emit KCFI assembly.
> * config/i386/i386.md: Add KCFI RTL substitution patterns.
> * doc/invoke.texi: Document x86 nuances.
> * opts.cc (common_handle_option): Reset flag_sanitize_kcfi_arity.
>
> gcc/testsuite/ChangeLog:
>
> * gcc.dg/kcfi/kcfi-adjacency.c: Add x86 patterns.
> * gcc.dg/kcfi/kcfi-basics.c: Add x86 patterns.
> * gcc.dg/kcfi/kcfi-call-sharing.c: Add x86 patterns.
> * gcc.dg/kcfi/kcfi-complex-addressing.c: Add x86 patterns.
> * gcc.dg/kcfi/kcfi-direct-call-shapes.c: Add x86 patterns.
> * gcc.dg/kcfi/kcfi-move-preservation.c: Add x86 patterns.
> * gcc.dg/kcfi/kcfi-no-sanitize-inline.c: Add x86 patterns.
> * gcc.dg/kcfi/kcfi-no-sanitize.c: Add x86 patterns.
> * gcc.dg/kcfi/kcfi-offset-validation.c: Add x86 patterns.
> * gcc.dg/kcfi/kcfi-patchable-entry-only.c: Add x86 patterns.
> * gcc.dg/kcfi/kcfi-patchable-large.c: Add x86 patterns.
> * gcc.dg/kcfi/kcfi-patchable-medium.c: Add x86 patterns.
> * gcc.dg/kcfi/kcfi-patchable-prefix-only.c: Add x86 patterns.
> * gcc.dg/kcfi/kcfi-tail-calls.c: Add x86 tail-call patterns.
> * gcc.dg/kcfi/kcfi-trap-section.c: Add x86 trap patterns.
> * gcc.dg/kcfi/kcfi-trap-section-per-func.c: New test.
> * gcc.dg/kcfi/kcfi-x86-32bit.c: New test.
> * gcc.dg/kcfi/kcfi-x86-arity.c: New test.
> * gcc.dg/kcfi/kcfi-x86-fixed-r10.c: New test.
> * gcc.dg/kcfi/kcfi-x86-fixed-r11.c: New test.
> * gcc.dg/kcfi/kcfi-x86-retpoline-r11.c: New test.
>
> Signed-off-by: Kees Cook <[email protected]>
[...]
> +/* Apply x86-64 specific masking to KCFI type ID. TYPE_ID is the 32-bit
> + KCFI type identifier to potentially mask. Returns the type ID with
> + x86-64 specific adjustments to avoid embedding ENDBR instruction
> + sequences in the type identifier values. */
> +
> +static uint32_t
> +ix86_kcfi_mask_type_id (uint32_t type_id)
> +{
> + /* Avoid embedding ENDBR instructions in KCFI type IDs.
> + ENDBR64: 0xfa1e0ff3, ENDBR32: 0xfb1e0ff3
> + If the type ID matches either instruction encoding, increment by 1. */
> + if (type_id == 0xfa1e0ff3U || type_id == 0xfb1e0ff3U)
This should use the ix86_endbr_immediate_operand predicate. Exactly
one target dependent immediate (0xfa1... for TARGET_64BIT and 0xfb1
for TARGET_32BIT) should be avoided only with flag_cf_protection &
CF_BRANCH. All this is handled in the predicate.
Uros.