Skip to content

CFI failure at bpf_obj_free_fields #2099

Open

Description

@samitolvanen

While running BPF self-tests with CONFIG_CFI_CLANG enabled I ran into a CFI failure in bpf_obj_free_fields:

CFI failure at bpf_obj_free_fields+0x190/0x238 (target: bpf_crypto_ctx_release+0x0/0x94; expected type: 0xa488ebfc)
Internal error: Oops - CFI: 00000000f2008228 [#1]  SMP
Modules linked in: bpf_testmod(OE) [last unloaded: bpf_testmod(OE)]
CPU: 3 UID: 0 PID: 58 Comm: kworker/u36:0 Tainted: G           OE       6.16.0-rc6-00004-g12b942bdb9aa #7 NONE 
Tainted: [O]=OOT_MODULE, [E]=UNSIGNED_MODULE
Hardware name: linux,dummy-virt (DT)
Workqueue: events_unbound bpf_map_free_deferred
pstate: 20400005 (nzCv daif +PAN -UAO -TCO -DIT -SSBS BTYPE=--)
pc : bpf_obj_free_fields+0x190/0x238
lr : bpf_obj_free_fields+0x170/0x238
sp : ffff800082d9bc40
x29: ffff800082d9bc40 x28: ffff800081d5a1f8 x27: ffffc1ffc0000000
x26: 0010000000000000 x25: 0000000000000030 x24: 0000000000000000
x23: ffff0000c35f9da0 x22: ffff0000c6c352c0 x21: ffff0000c35f9da0
x20: ffff0000c2fcd7f8 x19: ffff0000c35f9d80 x18: ffff800080d60df0
x17: 00000000a488ebfc x16: 00000000f6ad4468 x15: 0000000000000137
x14: 0000000000000137 x13: 0000000014f808bb x12: 00000000c9d55af7
x11: ffff0000c0aa0ac8 x10: 0000000000000001 x9 : ffff80008022f360
x8 : ffff8000802bbd78 x7 : ffff80008008e980 x6 : 0000000000000000
x5 : 0000000000000000 x4 : 0000000000000001 x3 : ffff8000814f2aeb
x2 : 0000000000000008 x1 : ffff0000c2fcd7f8 x0 : ffff0000c6c352c0
Call trace:
 bpf_obj_free_fields+0x190/0x238 (P)
 array_map_free+0x104/0x19c
 bpf_map_free_deferred+0x70/0x98
 process_one_work+0x240/0x598
 worker_thread+0x25c/0x3a8
 kthread+0x164/0x29c
 ret_from_fork+0x10/0x20
Code: 729d7f91 72b49111 6b11021f 54000040 (d4304500) 
---[ end trace 0000000000000000 ]---

bpf_obj_free_fields calls a destructor function via a btf_dtor_kfunc_t pointer, which accepts a void * argument, but the argument type for bpf_crypto_ctx_release is struct bpf_crypto_ctx *:

__bpf_kfunc void bpf_crypto_ctx_release(struct bpf_crypto_ctx *ctx)
typedef void (*btf_dtor_kfunc_t)(void *);

struct btf_field_kptr {
...
	/* dtor used if btf_is_kernel(btf), otherwise the type is
	 * program-allocated, dtor is NULL,  and __bpf_obj_drop_impl is used
	 */
	btf_dtor_kfunc_t dtor;
...
};

It would appear that this destructor call is going to always trip CFI because AFAICT none of the target functions actually accept a void * argument. I also see a similar failure in bpf_testmod, but I assume there are more:

CFI failure at bpf_obj_free_fields+0x190/0x238 (target: bpf_testmod_ctx_release+0x0/0x94 [bpf_testmod]; expected type: 0xa488ebfc)

A quick test to change the bpf_crypto_ctx_release argument type to void * instead didn't go far because apparently the verifier cannot determine the size of the argument:

libbpf: prog 'crypto_release': BPF program load failed: -EINVAL
libbpf: prog 'crypto_release': failed to load: -EINVAL
libbpf: failed to load object 'crypto_basic'
run_subtest:FAIL:unexpected_load_failure unexpected error: -22 (errno 22)
VERIFIER LOG:
=============
arg#0 reference type('UNKNOWN ') size cannot be determined: -22
0: R1=ctx() R10=fp0
; int crypto_release(void *ctx) @ crypto_basic.c:13
0: (b7) r1 = 16                       ; R1_w=16
; struct bpf_crypto_params params = { @ crypto_basic.c:15
...
73: (85) call bpf_crypto_ctx_release#50933
kernel function bpf_crypto_ctx_release args#0 pointer type UNKNOWN  is not supported

I'm afraid some kind of a union hack might be needed to resolve this mismatch.

Activity

  1. samitolvanen commented on Jul 16, 2025

    @samitolvanen
    MemberAuthor

    It looks like this has been in the BPF code for a while. It appears to have been introduced in commit 14a324f in v5.18. I wonder if something changed in self-tests because I don't remember seeing this CFI failure earlier.

  2. samitolvanen commented on Jul 24, 2025

    @samitolvanen
    MemberAuthor

    OK, looks like there are only three instances of mismatching destructor kfunc types in the kernel (so far). The easiest solution seems to be adding a destructor stub for the functions that are also exposed to BPF, and registering the stub as the destructor instead of the actual function.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions