freenode
Kernel & Low-Level

BPF exception unwind gains cleanup pads for Rust Drop

Yonghong Song's bpf-next series lets bpf_throw() run compiler-emitted landing pads so frames can release locks and owned objects on the way out.

A 20-patch series posted to the BPF list by Yonghong Song teaches the kernel to run exception cleanup landing pads when bpf_throw() unwinds a program. The change closes a long-standing gap that blocked Rust BPF programs from using throw as a panic path and forced C programs that hold locks or owned pointers to avoid throwing at all.

Today bpf_throw() walks the BPF call stack to the exception boundary and discards every frame in between. Anything a frame still owns (an RCU read lock, a preemption-disabled section, a referenced kernel pointer) is never released, so the verifier simply forbids a throw from such a frame. Rust Drop glue is exactly that release path, with nowhere to run.

LLVM 23 already emits the compiler side: an invoke whose cleanup landing pad holds the Drop call, plus a flat .bpf_cleanup table of (begin, end, landing_pad) byte-offset triples. The kernel half accepts that table at program load, teaches the verifier that a covered call may transfer to its pad, and has bpf_throw() execute matching pads as it walks. Pads terminate via a bpf_unwind_resume() kfunc that stands in for the usual _Unwind_Resume entry point.

Architecture support lands for x86-64 and arm64, forcing full callee-saved spills so the walker can recover frame state and dispatch pads. libbpf gains parsing of the compiler section, load-path plumbing for both ordinary and light-skeleton loads, linker acceptance of the related relocations, and name rewriting so _Unwind_Resume resolves to the kernel kfunc. Selftests spell out by hand, in naked assembly, the shapes a frontend would emit, covering successful multi-frame cleanup and a large set of shapes the kernel correctly rejects.

The practical result is that a frame can hold an RCU guard or similar resource across a call that may throw, and the unlock or Drop still runs on the unwind path. That is the missing piece for idiomatic Rust panic handling inside BPF, and for any C helper that needs deterministic release when an exception fires.