Re: [PATCH 2/2] x86/cfi,bpf: Fix BPF JIT call

From: Alexei Starovoitov
Date: Wed Nov 22 2023 - 19:43:21 EST


On Wed, Nov 22, 2023 at 3:15 AM Peter Zijlstra <peterz@xxxxxxxxxxxxx> wrote:
>
>
> To be very explicit, let me list all the various forms of function
> calls:
>
> Traditional:
>
> foo:
> ... code here ...
> ret
>
> direct caller:
>
> call foo
>
> indirect caller:
>
> lea foo(%rip), %r11
> call *%r11
>
> IBT:
>
> foo:
> endbr64
> ... code here ...
> ret
>
> direct caller:
>
> call foo / call foo+4
>
> indirect caller:
>
> lea foo(%rip), %r11
> ...
> call *%r11
>
>
> kCFI:
>
> __cfi_foo:
> movl $0x12345678, %rax
> (11 nops when CALL_PADDING)
> foo:
> endbr64 (when also IBT)
> ... code here ...
> ret
>
> direct caller:
>
> call foo / call foo+4
>
> indirect caller:
>
> lea foo(%rip), %r11
> ...
> movl $(-0x12345678), %r10d
> addl -15(%r11), %r10d (or -4 without CALL_PADDING)
> je 1f
> ud2
> 1:call *%r11
>
>
> FineIBT (builds as kCFI + CALL_PADDING + IBT + RETPOLINE and runtime
> patches things to look like):
>
> __cfi_foo:
> endbr64
> subl $0x12345678, %r10d
> jz foo
> ud2
> nop
> foo:
> osp nop3 (was endbr64)
> ... code here ...
> ret
>
> direct caller:
>
> call foo / call foo+4
>
> indirect caller:
>
> lea foo(%rip), %r11
> ...
> movl $0x12345678, %r10d
> subl $16, %r11
> nop4
> call *%r11

Got it. That helps a lot!
You kind of have this comment scattered through arch/x86/kernel/alternative.c
but having it in one place like above would go a long way.
Could you please add it to arch/x86/net/bpf_jit_comp.c
or arch/x86/include/asm/cfi.h next to enum cfi_mode ?

> > I'm not sure doing cfi_bpf_hash check in JITed code is completely solving the problem.
> > From bpf_dispatcher_*_func() calling into JITed will work,
> > but this emit_prologue() is doing the same job for all bpf progs.
> > Some bpf progs call each other directly and indirectly.
> > bpf_dispatcher_*_func() -> JITed_BPF_A -> JITed_BPF_B.
> > A into B can be a direct call (which cfi doesn't care about) and
> > indirect via emit_bpf_tail_call_indirect()->emit_indirect_jump().
> > Should we care about fineibt/kcfi there too?
>
> The way I understood the tail-call thing to work is that it jumps to
> bpf_prog + X86_TAIL_CALL_OFFSET, we already emit an extra ENDBR there to
> make this work.
>
> So the A -> B indirect call is otherwise unadornen and only needs ENDBR.
>
> Ideally that would use kCFI/FineIBT but since it also skips some of the
> setup, this gets to be non-trivial, so I've let this be as is.

I see. yeah. The setup is not trivial indeed. Keep as-is is fine.

> So the kCFI thing is 'new' but readily inspected by objdump or godbolt:
>
> https://godbolt.org/z/sGe18z3ca
>
> (@Sami, that .Ltmp15 thing, I don't see that in the kernel, what
> compiler flag makes that go away?)

I also noticed this discrepancy. It doesn't seem to be used.
Looks weird to spend 8 bytes to store -sizeof(ud2)

> As to FineIBT, that has a big comment in arch/x86/kernel/alternative.c
> where I rewrite the kCFI thing into FineIBT. I can refer there to avoid
> duplicating comments, would that work?

Just the above comment somewhere would work.
I wouldn't worry about duplication. This is tricky stuff.
When gcc folks get around implementing kcfi they will find it useful too.