[clang] [llvm] [X86] Enhance kCFI type IDs with a 3-bit arity indicator. (PR #117121)
Scott Constable via cfe-commits
cfe-commits at lists.llvm.org
Wed Dec 4 10:34:46 PST 2024
scottconstable wrote:
> I think Scott's second point may be the relevant one - this may be similar strength or slightly weaker, but having an indicator stating which registers are live is potentially needed to allow FineIBT to poison them during speculative execution.
>
> Given how much padding is used in the X86 kernel around function headers, have you considered just stealing another byte to encode the data for your arity stuff, and considering it to be a separate mitigation from KCFI tags?
Yes, and this is what our first prototype does. There is a brief writeup here: https://lkml.org/lkml/2024/9/27/982.
When I started to refine the prototype into a PR, I noticed that stealing another byte in the padding region causes problems for either LLVM or for Linux. This is because LLVM emits function headers like this:
```
NOP
NOP
...
MOV $01234567, %eax
<foo>:
```
But when the Linux image is loaded, it moves the type information to the *beginning* of the padding, to allow the pre-function padding to be used for hot patching:
```
MOV $01234567, %eax
...
NOP
NOP
<foo>:
```
Thus, when new metadata is added to the padding region, it introduces new challenges for either LLVM or for Linux:
- If the metadata is added *before* the hash, then Linux needs to know that it might need to look for the hash in a different location, i.e., in a different offset relative to the start of the function.
- If the metadata is added *after* the hash, then LLVM's KCFI code generator needs to correctly adjust the offsets for the call site KCFI checks, depending on whether the new metadata is being generated.
The discussions on LKML also led us to simplify the proposal, which originally had called for an 8-bit field to denote which registers are pointers, so that only pointers would be poisoned after a misprediction. The new proposal poisons all live registers and therefore requires a 3-bit field to record the arity (defined as the number of live registers). It occurred to me that arity might also be useful to harden the hash checks for KCFI, but that may require a consensus among KCFI stakeholders.
One other alternative could be to use the 3-bit reg field in the MOV to encode the arity. For example:
```
MOV $01234567, %eax # arity 0
MOV $01234567, %ecx # arity 1
MOV $01234567, %edx # arity 2
MOV $01234567, %ebx # arity 3
MOV $01234567, %esp # arity 4
MOV $01234567, %ebp # arity 5
MOV $01234567, %esi # arity 6
MOV $01234567, %edi # arity 7+
```
I believe that this alternative implementation would also meet the requirements for the FineIBT register poisoning enhancement.
> The rest of the CFI type ID scheme is arch-independent, but in order to know which _registers_ are in use, you need arch dependent information, because you care about the calling convention, packing rules, etc. This is part of why Ramon thought your design choices were odd above - this isn't really a CFI enhancement or modification, this is another piece of information you need for speculation defenses that occur at a different abstraction level.
I agree with your interpretation. And thank you and @rcvalle again for the ongoing feedback and discussion.
https://github.com/llvm/llvm-project/pull/117121
More information about the cfe-commits
mailing list