RE: [PATCH v4 09/10] x86/ibt: Implement FineIBT-BHI mitigation

Constable, Scott D
2025-02-26T00:04:59Z

Peter's example is perfectly valid. I can elaborate on the broader goals:

Microarchitectural attacks such as Branch History Injection (BHI) and Intra-mode Branch Target Injection (IMBTI) [1] can cause an indirect call to mispredict to an adversary-influenced target within the same hardware domain (e.g., within the kernel). Instructions at the mispredicted target may execute speculatively and potentially expose kernel data (e.g., to a user-mode adversary) through a microarchitectural covert channel such as CPU cache state.

CET-IBT [2] is a coarse-grained control-flow integrity (CFI) ISA extension that enforces that each indirect call (or indirect jump) must land on an ENDBR (end branch) instruction, even speculatively*. FineIBT is a software technique that refines CET-IBT by associating each function type with a 32-bit hash and enforcing (at the callee) that the hash of the caller's function pointer type matches the hash of the callee's function type. However, recent research [3] has demonstrated that the conditional branch that enforces FineIBT's hash check can be coerced to mispredict, potentially allowing an adversary to speculatively bypass the hash check:

fineibt_target:
ENDBR64
SUB R10d, 0x01234567
JZ .fineibt_target_body # Even if the hash check fails and ZF=0, this branch could still mispredict as taken
UD2
.fineibt_target_body:
...

The techniques demonstrated in [3] require the attacker to be able to control the contents of at least one live register at the mispredicted target. Therefore, this patch set introduces a sequence of CMOV instructions at each indirect-callable target that poisons every live register with data that the attacker cannot control whenever the FineIBT hash check fails, thus mitigating any potential attack.

The security provided by this scheme has been discussed in detail on an earlier thread [4].

[1] https://www.intel.com/content/www/us/en/developer/articles/technical/software-security-guidance/technical-documentation/branch-history-injection.html
[2] Intel Software Developer's Manual, Volume 1, Chapter 18
[3] https://www.vusec.net/projects/native-bhi/
[4] https://lore.kernel.org/lkml/20240927194925.707462984@infradead.org/
*There are some caveats for certain processors, see [1] for more info

Regards,

Scott Constable

-----Original Message-----
From: Peter Zijlstra <peterz@infradead.org>
Sent: Tuesday, February 25, 2025 1:12 AM
To: x86@kernel.org
Cc: linux-kernel@vger.kernel.org; Milburn, Alyssa <alyssa.milburn@intel.com>; Constable, Scott D <scott.d.constable@intel.com>; joao@overdrivepizza.com; andrew.cooper3@citrix.com; jpoimboe@kernel.org; jose.marchesi@oracle.com; hjl.tools@gmail.com; ndesaulniers@google.com; samitolvanen@google.com; nathan@kernel.org; ojeda@kernel.org; kees@kernel.org; alexei.starovoitov@gmail.com; mhiramat@kernel.org; jmill@asu.edu
Subject: Re: [PATCH v4 09/10] x86/ibt: Implement FineIBT-BHI mitigation

On Mon, Feb 24, 2025 at 01:37:12PM +0100, Peter Zijlstra wrote:
> While WAIT_FOR_ENDBR is specified to be a full speculation stop; it
> has been shown that some implementations are 'leaky' to such an extend
> that speculation can escape even the FineIBT preamble.
>
> To deal with this, add additional hardening to the FineIBT preamble.
>
> Notably, using a new LLVM feature:
>
>
> https://github.com/llvm/llvm-project/commit/e223485c9b38a5579991b8cebb
> 6a200153eee245
>
> which encodes the number of arguments in the kCFI preamble's register.
>
> Using this register<->arity mapping, have the FineIBT preamble CALL
> into a stub clobbering the relevant argument registers in the
> speculative case.
>
> (This is where Scott goes and gives more details...)

Scott, could you give a blurb here? Would the below cover things?

The basic setup, for 1 argument, is something like:

__bhi_args_1:
jne .Lud
cmovne %r10, %rdi
ret
int3

__cfi_foo:
endbr
subl $hash, %r10d
call __bhi_args_1
foo:
osp nop3
...

such that when speculation of an indirect call is maliciously steered here from a non-matching site, the hash check (__cfi_foo's SUB) will not match, resulting in !ZF and non-zero R10. Subsequently the __bhi_args stub will either hit #UD exception, which kills speculation, or when steered wrong, hit the cmovne, which will then clobber the argument register RDI with whatever non-zero garbage sits in R10. Making it much harder to control whatever foo normally does with its input argument.

Additionally, CFI hashes are randomized at boot, making it much harder still to predict/control the non-zero value.