Skip to content

Commit 9150046

Browse files
Wire tap: a capture length the 6.6 verifier can bound; the matrix starts at 6.6
The first matrix run put the floor at 6.6, not 6.1: 6.1 refuses the wire tap's TCX attach type at load, with zero instructions verified, and every other object loads there. The socket tap's iov_iter reads are CO-RE guarded, so its 6.4 note is for the build host only. On 6.6 the wire tap was rejected inside emit(): "R4 invalid zero-sized read: u64=[0,4094]" at bpf_skb_load_bytes. The size was guarded by `cap && ...`, but a verifier before 6.9 does not narrow a register on the `!= 0` branch, so it still saw a possible zero. Submit the empty record first, then rebuild the bound by arithmetic the verifier does track: ((cap - 1) & CAP_MASK) + 1 is [1, CAP_MASK + 1] and an identity on the range that reaches it; barrier_var keeps clang from folding it away. Matrix: 6.6, 6.12, 6.18, 7.2 gate; bpf-next runs with continue-on-error. On 7.3-rc4 the walk VM's bpf_loop callbacks cost 850k verifier instructions against 14k on 6.12 and hit the 1M limit. That is worth seeing on every run, but a moving target should not block a merge until the change ships in a release. The local harness defaults to the same spread.
1 parent 241e169 commit 9150046

4 files changed

Lines changed: 47 additions & 19 deletions

File tree

‎.github/workflows/kernel-matrix.yml‎

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,15 @@ name: kernel-matrix
2525
# tracking the latest build, with no tag to bump and immune to quay's pruning of
2626
# old stamps.
2727
#
28-
# Expected failures are part of the signal: the wire tap attaches with TCX
29-
# (6.6+), so a rejection of it on 6.1 marks the floor, not a regression. The
30-
# legacy socket tap names `iov_iter.__iov`, which only exists from 6.4, but
31-
# its reads are CO-RE guarded, so it loads on older kernels; the 6.4 floor is
32-
# for the build host, since vmlinux.h is generated from the runner's own BTF
33-
# by `make bpf`. ubuntu-latest is well past it.
28+
# The floor is 6.6: the wire tap attaches with TCX, which 6.1 refuses at load
29+
# (zero instructions verified), and every other object loads on 6.1 — the
30+
# legacy socket tap names `iov_iter.__iov` (6.4+) but its reads are CO-RE
31+
# guarded, so that 6.4 floor is for the build host only, where vmlinux.h is
32+
# generated from the runner's own BTF by `make bpf`. So the gating lines are
33+
# the LTS and stable kernels from 6.6 up. bpf-next is run but does not gate:
34+
# it is a moving target, and 7.3-rc4 made the walk VM's bpf_loop callbacks
35+
# cost 850k verifier instructions against 14k on 6.12, which is worth seeing
36+
# but not worth blocking a merge on until it lands in a release.
3437

3538
on:
3639
workflow_dispatch:
@@ -50,11 +53,18 @@ jobs:
5053
# Kernel lines to verify. Each is resolved to its newest date-stamped
5154
# lvh image at run time (see the header).
5255
kernel:
53-
- '6.1'
5456
- '6.6'
5557
- '6.12'
58+
- '6.18'
59+
- '7.2'
5660
- 'bpf-next'
61+
# bpf-next is informational: a failure there is recorded in the grid
62+
# but does not fail the job, so the summary's gate ignores it.
63+
include:
64+
- kernel: 'bpf-next'
65+
informational: true
5766
name: kernel ${{ matrix.kernel }}
67+
continue-on-error: ${{ matrix.informational == true }}
5868
steps:
5969
- uses: actions/checkout@v4
6070

@@ -197,7 +207,7 @@ jobs:
197207
cells.append("❌"); fail += 1
198208
print(f"| `{obj}` | `{prog}` | " + " | ".join(cells) + " |")
199209
print()
200-
print("✅ accepted · ❌ rejected · ⚪ not run\n")
210+
print("✅ accepted · ❌ rejected · ⚪ not run · bpf-next is informational and does not gate\n")
201211
total = len(progs) * len([k for k in kernels if data[k] is not None])
202212
verb = "all programs loaded on every kernel" if fail == 0 else f"{fail} of {total} program×kernel checks failed"
203213
print(f"**{len(progs)} program(s) × {len(kernels)} kernel(s) — {verb}.**")

‎README.md‎

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -467,13 +467,20 @@ buffer held before — a response head where curl's body should be.
467467
yeetkit dependency is by absolute path for now.
468468
- **Kernel matrix**: `.github/workflows/kernel-matrix.yml` (the one
469469
`yeet new` scaffolds, adapted to this repo's per-directory objects)
470-
builds every `bin/*.bpf.o` on the runner, boots 6.1, 6.6, 6.12 and
471-
bpf-next under cilium's little-vm-helper, and runs the vendored static
472-
veristat in each VM via `build/verify-kernel.sh`. The summary is a grid
473-
of (object, program) × kernel, since `peer_sendmsg`/`peer_recvmsg`
474-
recur across the TLS taps. The socket tap loads on 6.1 despite the
475-
header note above: its `iov_iter` reads are CO-RE guarded, so the 6.4
476-
floor is for the build host only. `make veristat-matrix` runs the same thing
470+
builds every `bin/*.bpf.o` on the runner, boots 6.6, 6.12, 6.18, 7.2
471+
and bpf-next under cilium's little-vm-helper, and runs the vendored
472+
static veristat in each VM via `build/verify-kernel.sh`. The summary
473+
is a grid of (object, program) × kernel, since
474+
`peer_sendmsg`/`peer_recvmsg` recur across the TLS taps. The floor is
475+
6.6: 6.1 refuses the wire tap's TCX attach type at load, and every
476+
other object loads there (the socket tap's `iov_iter` reads are CO-RE
477+
guarded, so the 6.4 note above is for the build host only). The first
478+
run also caught the 6.6 verifier rejecting the wire tap's
479+
`bpf_skb_load_bytes` size as possibly zero, since a verifier before
480+
6.9 does not narrow a register on a `!= 0` branch; the bound is now
481+
rebuilt by arithmetic. bpf-next runs but does not gate: on 7.3-rc4 the
482+
walk VM's `bpf_loop` callbacks cost 850k verifier instructions against
483+
14k on 6.12 and hit the one-million limit. `make veristat-matrix` runs the same thing
477484
locally with lvh + a static qemu (Linux, KVM, root for the VM), and
478485
`make veristat` is the single-kernel check against this host.
479486

‎bpf/wire/wire.bpf.c‎

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -208,9 +208,20 @@ static __always_inline void emit(struct __sk_buff *skb, struct pkt *p, __u8 hook
208208
__u32 cap = len > off ? len - off : 0;
209209
if (cap > CAP_MASK)
210210
cap = CAP_MASK;
211+
if (cap == 0) {
212+
e->cap_len = 0;
213+
bpf_ringbuf_submit(e, 0);
214+
return;
215+
}
216+
/* bpf_skb_load_bytes refuses a size that may be zero, and a
217+
* verifier before 6.9 does not narrow a register on the
218+
* `!= 0` branch above (6.6 rejects: "R4 invalid zero-sized
219+
* read: u64=[0,4094]"). Rebuild the bound by arithmetic it
220+
* does track: [1, CAP_MASK + 1], an identity on [1, CAP_MASK].
221+
* The barrier keeps clang from folding it away. */
211222
barrier_var(cap);
212-
cap &= CAP_MASK;
213-
if (cap && bpf_skb_load_bytes(skb, p->data_off + off, e->data, cap))
223+
cap = ((cap - 1) & CAP_MASK) + 1;
224+
if (bpf_skb_load_bytes(skb, p->data_off + off, e->data, cap))
214225
cap = 0;
215226
e->cap_len = cap;
216227
bpf_ringbuf_submit(e, 0);

‎build/kernel-matrix.sh‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
# matrix.yml. This is a TEST HARNESS, not part of the build, and needs a
55
# Linux host (ideally with /dev/kvm; without it QEMU falls back to slow TCG).
66
#
7-
# build/kernel-matrix.sh [kernel ...] # default: an LTS spread + bpf-next
7+
# build/kernel-matrix.sh [kernel ...] # default: 6.6 (the TCX floor) up, + bpf-next
88
# make veristat-matrix # same, via the Makefile
99
#
1010
# It uses cilium's lvh + QEMU to boot quay.io/lvh-images/kind:<kernel> images.
@@ -18,7 +18,7 @@
1818

1919
set -eu
2020

21-
KERNELS=${*:-"5.10-main 5.15-main 6.1-main 6.6-main 6.12-main bpf-next-main"}
21+
KERNELS=${*:-"6.6-main 6.12-main 6.18-main 7.2-main bpf-next-main"}
2222
LVH_VERSION="${LVH_VERSION:-v0.0.30}"
2323
SSH_PORT="${SSH_PORT:-2222}"
2424
MON_PORT="${MON_PORT:-45454}"

0 commit comments

Comments
 (0)