[PATCH bpf-next 0/1] Support kCFI + BPF on riscv64

Puranjay Mohan
2024-03-03T17:02:42Z

With CONFIG_CFI_CLANG, the compiler injects a type preamble immediately
before each function and a check to validate the target function type
before indirect calls:

; type preamble
.word <id>
function:
...
; indirect call check
lw t1, -4(a0)
lui t2, <hi20>
addiw t2, t2, <lo12>
beq t1, t2, .Ltmp0
ebreak
.Ltmp0:
jarl a0

BPF JIT currently doesn't emit this preamble before BPF programs and when
the calling fuction tries to load the type id from the preamble, it finds
an invalid value there.

This will cause CFI failures like in the following bpf selftest:

root@rv-selftester:~/bpf# ./test_progs -a "rbtree_success"

CFI failure at bpf_rbtree_add_impl+0x148/0x350 (target: bpf_prog_fb8b097ab47d164a_less+0x0/0x42; expected type: 0x00000000)
WARNING: CPU: 1 PID: 278 at bpf_rbtree_add_impl+0x148/0x350
Modules linked in: bpf_testmod(OE) drm fuse dm_mod backlight i2c_core configfs drm_panel_orientation_quirks ip_tables x_tables
CPU: 1 PID: 278 Comm: test_progs Tainted: P OE 6.8.0-rc1 #1
Hardware name: riscv-virtio,qemu (DT)
epc : bpf_rbtree_add_impl+0x148/0x350
ra : bpf_prog_27b36e47d273751e_rbtree_first_and_remove+0x1aa/0x35e
epc : ffffffff805acc0c ra : ffffffff780077fa sp : ff2000000110b9d0
gp : ffffffff868d6218 tp : ff60000085772a40 t0 : ffffffff86849660
t1 : 0000000000000000 t2 : ffffffff9e4709a9 s0 : ff2000000110ba50
s1 : ff60000089c14958 a0 : ff60000089c14758 a1 : ff60000089c14958
a2 : 0000000000000000 a3 : 0000000000000000 a4 : 0000000000000000
a5 : 0000000000000000 a6 : ff6000008aba4b30 a7 : ffffffff86849640
s2 : ff6000008aba4b30 s3 : ff60000089c14758 s4 : ffffffff780079f0
s5 : 0000000000000000 s6 : ffffffff84c01080 s7 : ff6000008aba4b30
s8 : 0000000000000000 s9 : 0000000000000000 s10: 0000000000000001
s11: 0000000000000000 t3 : ffffffff868499e0 t4 : ffffffff868499c0
t5 : ffffffff86849840 t6 : ffffffff86849860
status: 0000000200000100 badaddr: 0000000000000000 cause: 0000000000000003
[<ffffffff805acc0c>] bpf_rbtree_add_impl+0x148/0x350
[<ffffffff780077fa>] bpf_prog_27b36e47d273751e_rbtree_first_and_remove+0x1aa/0x35e
[<ffffffff8294f32c>] bpf_test_run+0x2a4/0xa3c
[<ffffffff8294d032>] bpf_prog_test_run_skb+0x47a/0xe52
[<ffffffff805083ee>] bpf_prog_test_run+0x170/0x548
[<ffffffff805029c8>] __sys_bpf+0x2d2/0x378
[<ffffffff804ff570>] __riscv_sys_bpf+0x5c/0x120
[<ffffffff8000e8fe>] syscall_handler+0x62/0xe4
[<ffffffff83362df6>] do_trap_ecall_u+0xc6/0x27c
[<ffffffff833822c4>] ret_from_exception+0x0/0x64
---[ end trace 0000000000000000 ]---

The calling function tries to load the type id hash from target_func - 4.
If this memory address is not mapped then it can cause a page fault and
crash the kernel.

This behaviour can be seen by running the 'dummy_st_ops' selftest:

root@rv-selftester:~/bpf# ./test_progs -a dummy_st_ops

Unable to handle kernel paging request at virtual address ffffffff78204ffc
Oops [#1]
Modules linked in: bpf_testmod(OE) drm fuse backlight i2c_core drm_panel_orientation_quirks dm_mod configfs ip_tables x_tables [last unloaded: bpf_testmod(OE)]
CPU: 3 PID: 356 Comm: test_progs Tainted: P OE 6.8.0-rc1 #1
Hardware name: riscv-virtio,qemu (DT)
epc : bpf_struct_ops_test_run+0x28c/0x5fc
ra : bpf_struct_ops_test_run+0x26c/0x5fc
epc : ffffffff82958010 ra : ffffffff82957ff0 sp : ff200000007abc80
gp : ffffffff868d6218 tp : ff6000008d87b840 t0 : 000000000000000f
t1 : 0000000000000000 t2 : 000000002005793e s0 : ff200000007abcf0
s1 : ff6000008a90fee0 a0 : 0000000000000000 a1 : 0000000000000000
a2 : 0000000000000000 a3 : 0000000000000000 a4 : 0000000000000000
a5 : ffffffff868dba26 a6 : 0000000000000001 a7 : 0000000052464e43
s2 : 00007ffffc0a95f0 s3 : ff6000008a90fe80 s4 : ff60000084c24c00
s5 : ffffffff78205000 s6 : ff60000088750648 s7 : ff20000000035008
s8 : fffffffffffffff4 s9 : ffffffff86200610 s10: 0000000000000000
s11: 0000000000000000 t3 : ffffffff8483dc30 t4 : ffffffff8483dc10
t5 : ffffffff8483dbf0 t6 : ffffffff8483dbd0
status: 0000000200000120 badaddr: ffffffff78204ffc cause: 000000000000000d
[<ffffffff82958010>] bpf_struct_ops_test_run+0x28c/0x5fc
[<ffffffff805083ee>] bpf_prog_test_run+0x170/0x548
[<ffffffff805029c8>] __sys_bpf+0x2d2/0x378
[<ffffffff804ff570>] __riscv_sys_bpf+0x5c/0x120
[<ffffffff8000e8fe>] syscall_handler+0x62/0xe4
[<ffffffff83362df6>] do_trap_ecall_u+0xc6/0x27c
[<ffffffff833822c4>] ret_from_exception+0x0/0x64
Code: b603 0109 b683 0189 b703 0209 8493 0609 157d 8d65 (a303) ffca
---[ end trace 0000000000000000 ]---
Kernel panic - not syncing: Fatal exception
SMP: stopping secondary CPUs

This patch improves the BPF JIT for the riscv64 architecture to emit kCFI
type id before BPF programs and struct ops trampolines.

After applying this patch, the above two selftests pass without any issues.

root@rv-selftester:~/bpf# ./test_progs -a "rbtree_success,dummy_st_ops"
#70/1 dummy_st_ops/dummy_st_ops_attach:OK
#70/2 dummy_st_ops/dummy_init_ret_value:OK
#70/3 dummy_st_ops/dummy_init_ptr_arg:OK
#70/4 dummy_st_ops/dummy_multiple_args:OK
#70/5 dummy_st_ops/dummy_sleepable:OK
#70/6 dummy_st_ops/test_unsupported_field_sleepable:OK
#70 dummy_st_ops:OK
#189/1 rbtree_success/rbtree_add_nodes:OK
#189/2 rbtree_success/rbtree_add_and_remove:OK
#189/3 rbtree_success/rbtree_first_and_remove:OK
#189/4 rbtree_success/rbtree_api_release_aliasing:OK
#189 rbtree_success:OK
Summary: 2/10 PASSED, 0 SKIPPED, 0 FAILED

root@rv-selftester:~/bpf# zcat /proc/config.gz | grep CONFIG_CFI_CLANG
CONFIG_CFI_CLANG=y

Puranjay Mohan (1):
riscv64/cfi,bpf: Support kCFI + BPF on riscv64

arch/riscv/include/asm/cfi.h | 17 +++++++++++
arch/riscv/kernel/cfi.c | 53 +++++++++++++++++++++++++++++++++
arch/riscv/net/bpf_jit.h | 2 +-
arch/riscv/net/bpf_jit_comp32.c | 2 +-
arch/riscv/net/bpf_jit_comp64.c | 14 ++++++++-
arch/riscv/net/bpf_jit_core.c | 9 +++---
6 files changed, 90 insertions(+), 7 deletions(-)

--
2.40.1