Fix BPF verifier "zero-sized read" rejection on Linux 6.12+#5
Open
jevansnyc wants to merge 1 commit into
Open
Conversation
bpf_skb_load_bytes()'s length argument is ARG_CONST_SIZE, so the verifier
requires it to be provably 1..sizeof(dst). clang kept a parallel,
un-narrowed copy of `cap` (kept live to store e->captured) and passed that
register as the length, so stricter verifiers (Linux 6.12+, observed on
6.12.73 aarch64) reject the capture read with:
R4 invalid zero-sized read: u64=[0,510] @ httptop.bpf.c
Route the length through a volatile stack slot and bound it (1..DATA_MAX)
immediately before the load, so the register the helper sees is a fresh
memory reload the verifier can only narrow via that adjacent check. Note:
a barrier_var() and reordering the e->captured store alone did NOT fix it
-- clang still sourced the helper length from the stale, un-narrowed
register; the volatile reload is what forces the bound onto the exact
register passed.
The dropped `cap &= (DATA_MAX - 1)` mask is now redundant: the explicit
`rlen > DATA_MAX` bound supersedes it.
Verified on Linux 6.12.73 aarch64 with `yeet run verify.js` capturing
live loopback HTTP.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
julian-goldstein
approved these changes
Jun 23, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
make && yeet run .fails to load the eBPF program on newer kernels. On Linux 6.12.73 (aarch64) the verifier rejects the capture read with:bpf_skb_load_bytes()'s length argument isARG_CONST_SIZE, so the verifier requires it to be provably1..sizeof(dst). The existingcap &= (DATA_MAX-1); if (cap == 0) return;guard is logically correct, but clang keeps a parallel, un-narrowed copy ofcapalive (to storee->captured) and passes that register as the helper length. The instruction trace shows thecap == 0check narrowing one register while the call sources the length from another that still ranges[0,510].Fix
Route the length through a
volatilestack slot and bound it (1..DATA_MAX) immediately before the load. The volatile reload forces the value the helper sees to be a fresh memory load that the verifier can only narrow via the adjacent check — so the exact register passed is provably in range.Things that did not work (documented in the commit, to save the next person the detour):
barrier_var(cap)before the guard — clang still kept a stale copy.e->captured = capafter the guard — same.The dropped
cap &= (DATA_MAX - 1)mask is now redundant; the explicitrlen > DATA_MAXbound supersedes it.Testing
Verified on Linux 6.12.73 aarch64 with the repo's own headless check, capturing live loopback HTTP:
Note
Draft: I only have a 6.12.73/aarch64 box to test on. Worth a sanity check that the program still loads on the older kernels/arches the prebuilt object was originally validated against (CO-RE should keep it portable, but the verifier behavior that triggered this is version-dependent).
🤖 Generated with Claude Code