Linux Hardening
 help / color / mirror / Atom feed
From: Kees Cook <kees@kernel.org>
To: Jeff Law <jeffrey.law@oss.qualcomm.com>
Cc: Andrea Pinski <andrew.pinski@oss.qualcomm.com>,
	Richard Biener <rguenther@suse.de>,
	Jeffrey Law <jefflaw@qti.qualcomm.com>,
	Joseph Myers <josmyers@redhat.com>,
	Jakub Jelinek <jakub@redhat.com>,
	Martin Uecker <uecker@tugraz.at>,
	Peter Zijlstra <peterz@infradead.org>,
	Ard Biesheuvel <ardb@kernel.org>, Jan Hubicka <hubicka@ucw.cz>,
	Uros Bizjak <ubizjak@gmail.com>,
	Richard Earnshaw <richard.earnshaw@arm.com>,
	Richard Sandiford <richard.sandiford@arm.com>,
	Marcus Shawcroft <marcus.shawcroft@arm.com>,
	Kyrylo Tkachov <kyrylo.tkachov@arm.com>,
	Kito Cheng <kito.cheng@gmail.com>,
	Palmer Dabbelt <palmer@dabbelt.com>,
	Andrew Waterman <andrew@sifive.com>,
	Jim Wilson <jim.wilson.gcc@gmail.com>,
	Juergen Christ <jchrist@linux.ibm.com>,
	Dan Li <ashimida.1990@gmail.com>,
	Sami Tolvanen <samitolvanen@google.com>,
	Ramon de C Valle <rcvalle@google.com>,
	Joao Moreira <joao@overdrivepizza.com>,
	Nathan Chancellor <nathan@kernel.org>,
	Bill Wendling <morbo@google.com>,
	Osterlund Sebastian <sebastian.osterlund@intel.com>,
	Constable Scott D <scott.d.constable@intel.com>,
	gcc-patches@gcc.gnu.org, linux-hardening@vger.kernel.org
Subject: Re: [PATCH v17 2/7] kcfi: Add core Kernel Control Flow Integrity infrastructure
Date: Sat, 10 Oct 2026 01:10:34 -0700	[thread overview]
Message-ID: <20261010080449.fe2893-kees@kernel.org> (raw)
In-Reply-To: <65c57069-0f3e-44e0-9562-8867548a7443@oss.qualcomm.com>

On Fri, Oct 09, 2026 at 07:35:29AM -0600, Jeff Law wrote:
> > +#include "coretypes.h"
> > +#include "rtl.h"
> In general we try to avoid having .h files include other .h files.
> Instead push these into the appropriate .cc files.  This make it easier

Ah-ha, understood! I've fixed this for v18 now.

> > +extern void kcfi_emit_traps_section (FILE *file, rtx trap_label_sym,
> > +				      int labelno);
> > +
> > +/* Extract KCFI type ID from current GIMPLE statement.  */
> > +extern rtx internal_kcfi_get_type_id_for_expanding_gimple_call (void);
> Pick one style.  You've got the parameter names in one prototype, but
> not the other.  The general preference within GCC is without the name.

I've fixed this for v18.  (I'm used to the opposite in Linux: everything
has parameter names.)

> > +- The check-call instruction sequence must be treated as a single unit: it
> > +  cannot be rearranged or split or optimized.  The pattern is that
> > +  indirect calls, "call *%target", get converted into:
[...]
> So I sound like a broken record at this point.  I see the assertion that
> it has to be an atomic unit, but nothing about why.

Yes, sorry, and I've answered this in detail now in my reply to 7/7. In
short:

1. The checked target must stay in the checked register until the call:
   a spill and reload in between would let attacker-writable memory
   supply an unchecked target ("pop %target; call %target").
2. The trap isn't noreturn: in the kernel's permissive mode, execution
   continues from the trap into the call, so the trap block can't be
   moved out of line.
3. The kernel depends on the exact layout: its trap handlers decode the
   instructions around the trap, and x86 may rewrite call sites in place
   into FineIBT at boot.

v18 will add all of this to the design comment, so the "why" lives next
to the assertion.

> > +  kcfi_nop = xstrdup (get_insn_template (code_num, nop_insn));
> > +  int nop_insn_bytes = get_attr_length (nop_insn);
> Note that some ports do not track insn lengths, so you'll consistently
> get back 0 on those.   I would probably recommend asserting the size is
> nonzero so that if you try to use this on a target without lengths
> you'll get a compile-time error rather than a runtime surprise.
>
> Also note that length computations are not guaranteed to be correct;
> they are guaranteed to conservatively correct for things like
> jump/branch relaxing to work.  So in that context conservatively correct
> means the reported size is no smaller than the actual size.
>
> So you need to make sure you're safe if the port reports back a longer
> size than what gets encoded into the final object.

Yup, I was able to trigger this mismatch with RISC-V's C extension
where I get a length of 4, but the assembler emits c.nop (2 bytes),
so with prefix NOPs the call sites loaded the type ID from the wrong
offset. (The kernel doesn't use prefix NOPs with KCFI on RISC-V, which
is why I never hit this.)  v18 will add a TARGET_KCFI_NOP_SIZE hook for
the size the assembler actually emits; RISC-V will return 2 with C or
Zca, as Clang does. Without the hook, KCFI will fall back to the length
attribute and assert that it's nonzero.

> > +  if (align_functions.levels[0].log <= 0)
> > +    return;
> So what happens if the port does not align functions to a useful boundary?

That padding is only used in Linux for x86's FineIBT live-patching,
which needs the __cfi_ label itself to be callable, so the kernel asks
for it with -falign-functions. But since this padding is supported by
the core KCFI code, it needs to work on all backends.  On RISC-V with
C, and on Thumb at -Os, the type ID could end up only 2-byte aligned,
making the load misaligned. Clang aligns every function that has a KCFI
type ID to at least 4 bytes, on all targets; v18 will do the same now.

> > +  /* Ignore cold partition sections: cold partitions are never indirect call
> > +     targets.  Only skip preambles for cold partitions (has_bb_partition = true)
> > +     not for entire cold-attributed functions (has_bb_partition = false).  */
> > +  if (in_cold_section_p && crtl && crtl->has_bb_partition)
> > +    return;
> Is that an design decision in the partitioning code or is it an
> implementation detail.  If it's the former, than I'm inclined to allow
> relying on it.  If it's the latter, then it seems we're just setting up
> a ticking time bomb.  It could well be either, I don't know off the top
> of my head.

It's a design property of partitioning: the cold part of a function is
never called, only jumped to from its parent. A function's address is
always its hot entry, so there's no valid entry point into the middle
of a function, and we don't want KCFI to create one.

bb-reorder enforces this: sanitize_hot_paths() walks up from every hot
block to the entry and moves that path into the hot partition, so when a
function is split, its entry block is always hot.

And x86 IBT already depends on the same property. The ENDBR inserter
(rest_of_insert_endbr_and_patchable_area() in i386-features.cc) only
puts an ENDBR at the head of the function's first block, never at the
start of the cold partition. An indirect branch into a cold partition
would fault under IBT just as it would fail a KCFI check. For example,
with -O2 -fcf-protection=full -fsanitize=kcfi:

	__cfi_work:
		...
		movl	$0xb8360626, %eax
	work:
		endbr64
		testl	%edi, %edi
		js	.L4
		...
		.section	.text.unlikely
	work.cold:
	.L4:
		subq	$24, %rsp

work gets both the ENDBR and the KCFI preamble; work.cold gets neither,
and is reached only by the direct "js .L4".

> I'm assuming that things like devirtualizing a call into a direct call
> are safe, including the speculative forms.

Yes. A call that becomes direct needs no check, since its target is
immutable. A speculative call keeps the original indirect call as the
fallback, and that is still an indirect call with its function type, so
it gets checked. KCFI is C-only, so in practice the speculative form
comes from indirect call profiling; v18 will add a tree-prof test
(gcc.dg/tree-prof/kcfi-speculative-call.c) that checks the fallback
keeps its KCFI check and that the profiled program runs without
trapping.

> Given we can pretty easily describe the generic call setup in RTL, how
> painful would it be to instead do the instrumentation in a generic way
> rather than twiddling each backend?
>
> In that kind of design we'd force the target to provide the two marker
> insns which mark the beginning and end of the atomic region. Target
> independent code would be used to load the typeid hash from the preamble
> (using the offset the target provides), compare it to the expected type
> id, branch around a trap-like insn, the trap-like insn itself, then
> finally the call.  I'm not demanding this rethink, but I do want it to
> be at least considered as a design question.

I did consider it, and tried something like it around v2. Two things
stopped me:

- The marker insns don't make the region atomic in the ways that matter
  (spill code between the check and the call, and the trap block being
  moved), as above.

- Each target's call setup is more than the generic call: the call
  expanders handle calling conventions, sibcalls, PLT and long-call
  forms, BTI and SLS mitigations, and more. KCFI wraps the call pattern
  each target already produces, so all of that keeps working, and the
  per-target part is only the check sequence itself (and the kernel's
  trap handlers decode a different sequence on each architecture
  anyway). I described this to Andrea around v5[1].

Clang made the same choice: generic code marks the indirect calls, and
each target expands its own check sequence just before the call.

Thanks for looking at all this; hopefully I've covered everything.
Let me know if I need to dig in further anywhere or if I've misunderstood
anything. :)

-Kees

[1] https://inbox.sourceware.org/gcc-patches/202510221253.D6B51F950@keescook/

-- 
Kees Cook

  reply	other threads:[~2026-10-10  8:10 UTC|newest]

Thread overview: 12+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-05 15:40 [PATCH v17 0/7] Introduce Kernel Control Flow Integrity ABI [PR107048] Kees Cook
2026-10-05 15:40 ` [PATCH v17 1/7] kcfi: Introduce KCFI typeinfo mangling API Kees Cook
2026-10-05 15:40 ` [PATCH v17 2/7] kcfi: Add core Kernel Control Flow Integrity infrastructure Kees Cook
2026-10-09 13:35   ` Jeff Law
2026-10-10  8:10     ` Kees Cook [this message]
2026-10-05 15:40 ` [PATCH v17 3/7] kcfi: Add regression test suite Kees Cook
2026-10-05 15:40 ` [PATCH v17 4/7] x86: Add x86_64 Kernel Control Flow Integrity implementation Kees Cook
2026-10-05 15:40 ` [PATCH v17 5/7] aarch64: Add AArch64 " Kees Cook
2026-10-05 15:40 ` [PATCH v17 6/7] arm: Add ARM 32-bit " Kees Cook
2026-10-05 15:40 ` [PATCH v17 7/7] riscv: Add RISC-V " Kees Cook
2026-10-09  4:39   ` Jeff Law
2026-10-10  8:04     ` Kees Cook

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261010080449.fe2893-kees@kernel.org \
    --to=kees@kernel.org \
    --cc=andrew.pinski@oss.qualcomm.com \
    --cc=andrew@sifive.com \
    --cc=ardb@kernel.org \
    --cc=ashimida.1990@gmail.com \
    --cc=gcc-patches@gcc.gnu.org \
    --cc=hubicka@ucw.cz \
    --cc=jakub@redhat.com \
    --cc=jchrist@linux.ibm.com \
    --cc=jefflaw@qti.qualcomm.com \
    --cc=jeffrey.law@oss.qualcomm.com \
    --cc=jim.wilson.gcc@gmail.com \
    --cc=joao@overdrivepizza.com \
    --cc=josmyers@redhat.com \
    --cc=kito.cheng@gmail.com \
    --cc=kyrylo.tkachov@arm.com \
    --cc=linux-hardening@vger.kernel.org \
    --cc=marcus.shawcroft@arm.com \
    --cc=morbo@google.com \
    --cc=nathan@kernel.org \
    --cc=palmer@dabbelt.com \
    --cc=peterz@infradead.org \
    --cc=rcvalle@google.com \
    --cc=rguenther@suse.de \
    --cc=richard.earnshaw@arm.com \
    --cc=richard.sandiford@arm.com \
    --cc=samitolvanen@google.com \
    --cc=scott.d.constable@intel.com \
    --cc=sebastian.osterlund@intel.com \
    --cc=ubizjak@gmail.com \
    --cc=uecker@tugraz.at \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox