freenode
Kernel & Low-Level

BPF verifier scopes callback args to close kernel stack escapes

Temporary pointers from ringbuf drain and array map iteration could outlive their helpers, enabling arbitrary kernel read/write and stack leaks.

The Linux BPF verifier will track frame-scoped lifetimes for callback arguments after Ihor Solodrai fixed two helpers that handed eBPF programs pointers valid only for a single callback invocation.

Nicholas Carlini reported both flaws. bpf_user_ringbuf_drain() passes a dynptr over a sample that the helper releases as soon as the callback returns. A program could save that pointer, a slice, or a clone in callback context and use it afterward. Once the helper stack is reused, an escaped descriptor allows arbitrary kernel read and write. The gap has existed since the drain helper arrived in Linux 7.3.

Separately, bpf_for_each_map_elem() over array and per-CPU array maps passes the address of a u32 key held on the helper's own stack. Saving and later dereferencing that pointer leaked four bytes of kernel stack. Loads through map-key pointers are not fault-protected. Hash keys and map values are unchanged; their storage is not callback-local.

The verifier previously typed these pointers but did not bind them to the callback's lifetime. The fix gives each callback frame an id, makes short-lived arguments descend from it (so slices and clones follow), and invalidates the id and its descendants when the frame pops. New selftests reject parking dynptrs, data slices, clones, nested drain escapes, and array keys after the callback returns, while confirming hash keys and array values still verify.

Alexei Starovoitov suggested the approach. The fixes are marked for stable backports.