A standard primitive in network BPF programs is checking that data + expected_length <= data_end, bailing out if this condition doesn't hold. For example, revalidate_data in Cilium checks that the packet is long enough to contain L2 and L3 headers.
The issue happens when revalidate_data is called multiple times within a complex graph of branching. Attached is the minimal example of reproduction. Every part seems to be important: putting the body of the inline function lb4_extract_tuple directly into handle_ipv4 produces good bytecode, removing seemingly useless conditions that never happen (ret == TC_ACT_SHOT or ret == TC_ACT_REDIRECT) also produces good bytecode, but the whole thing together produces buggy bytecode.
As can be observed with Godbolt, Clang generated the following transformation on pkt and pkt_end pointers (ctx->data and ctx->data_end) for one of revalidate_data calls:
r2 <<= 32
r2 >>= 32
r1 <<= 32
r1 >>= 32
The other revalidate_data calls don't have this zero-extension sequence, but the one that produces it fails to pass the verifier.
A similar issue has been reported recently (#59908, where this pattern of bitshifts was produced under other circumstances), but my case is more severe, because these useless instructions also make the verifier reject the program.
A standard primitive in network BPF programs is checking that
data + expected_length <= data_end
, bailing out if this condition doesn't hold. For example,revalidate_data
in Cilium checks that the packet is long enough to contain L2 and L3 headers.The issue happens when
revalidate_data
is called multiple times within a complex graph of branching. Attached is the minimal example of reproduction. Every part seems to be important: putting the body of the inline functionlb4_extract_tuple
directly intohandle_ipv4
produces good bytecode, removing seemingly useless conditions that never happen (ret == TC_ACT_SHOT
orret == TC_ACT_REDIRECT
) also produces good bytecode, but the whole thing together produces buggy bytecode.Compilation and testing command line:
Verifier output:
As can be observed with Godbolt, Clang generated the following transformation on
pkt
andpkt_end
pointers (ctx->data
andctx->data_end
) for one ofrevalidate_data
calls:The other
revalidate_data
calls don't have this zero-extension sequence, but the one that produces it fails to pass the verifier.A similar issue has been reported recently (#59908, where this pattern of bitshifts was produced under other circumstances), but my case is more severe, because these useless instructions also make the verifier reject the program.