fix(ebpf): verifier-friendly bound in tls_openssl::probe_ssl_read - #711
Merged
Conversation
Kernel bump on the ubuntu-24.04 GHA runner between 2026-08-18 00:39
UTC (last-green m707) and 17:46 UTC (first-red m708) tightened the
verifier's scalar-range merge analysis. The `if payload_size < 512`
branch merges two scalar states with different upper bounds, which
the new verifier rejects with:
R2 min value is negative, either use unsigned or 'var &= const'
when the merged scalar reaches `bpf_probe_read_user`.
## Fix
Replace the if/else with an unconditional bitmask:
let capture_len = (payload_size & 0x1ff) as usize;
The bitmask gives an unconditional 0..=511 bound the verifier proves
in one pass without needing to widen through a branch merge.
## Behavioral impact
- Max capture drops from 512 → 511 bytes. The fragment buffer stays
512 bytes; the last byte stays zero. Downstream consumers already
handle short captures (payload_size < 512 is the common case).
- No behavior change for payload_size < 512.
- Payloads ≥ 512 bytes lose 1 byte of visibility (511 captured
instead of 512). Acceptable trade-off — SSL fragments are usually
well under 1 KiB and truncation was already flagged via
`payload_truncated`.
## Bracketing evidence
- Zero `waybill-ebpf/` commits since 2026-08-17 (`git log
--since="2026-08-17" -- waybill-ebpf/`)
- `bpf-linker` pinned at 0.11.0 (`.github/env/bpf-linker.env`)
- 3 consecutive main CI failures (post-#708, #709, #710) with
identical verifier trace
- Last-green main CI was #707 (2026-08-18 00:39 UTC); first-red
#708 (2026-08-18 17:46 UTC) — GHA runner bump falls in this
window
## Follow-on
- No new tests — the CI-level verifier acceptance IS the test.
- The pattern of "verifier tightened after runner bump" is now
documented for future incidents. Consider a periodic "canary"
test against nightly runner images to detect these drifts
proactively (out of scope for this fix).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
First attempt only masked ssl_read; ssl_write_entry had the same if/else pattern and was still rejected. Also — the initial bitmask alone wasn't enough because aya's `bpf_probe_read_user_buf` wrapper casts `dst.len() as u32` internally, and the verifier can't tie the slice length back to a proven bound (the shl 32; shr 32 zext in the trace's pc=93-94 is that cast). ## New approach Build the length through an explicit `u32` local with the bitmask, then construct the sub-slice via `slice::from_raw_parts_mut(ptr, capture_len_u32 as usize)` — bypassing the `&mut scratch_slice[..n]` bounds-check wrapper that generates the offending scalar range. The u32 local's provenance is clear to the verifier: it was constructed from `input & 0x1ff`, so it's in 0..=511. Passing this u32 → usize → slice.len() → u32 chain preserves the bound because each step is a widening/narrowing cast where the u32-provenance is visible. Applied to both call sites: `ssl_read_return` + `ssl_write_entry`. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This was referenced Aug 20, 2026
mlieberman85
added a commit
that referenced
this pull request
Aug 27, 2026
Cuts a stable v0.3.0. 43 commits since v0.2.0 with substantial feature additions: Feature highlights (grouped): * Resolvers + graph completeness: - m663 (#708, #709, #710, #712): local cache-probe resolver — reads ~/.m2, $GOMODCACHE, ~/.cargo, RubyGems, npm, pip caches for high-confidence PURL extraction without network. New C152 `waybill:resolver-tier` doc-scope annotation. - m235 (#688-#701): Gradle transitive dep-graph ladder — four-tier resolution (subprocess → cache → static → lockfile-only) with C146-C150 doc-scope + per-component annotations. - m236 (#703-#706): universalize `waybill:unresolved-reason` (C151) across all design-tier readers. * Architecture: - m664 (#715): single-pass filesystem walker — consolidates 21+ ecosystem-reader walk sites into one tree traversal per scan. 5-6× warm-cache perf improvement on large fixtures. - m665 (#719): `--no-binary-scan=<MODE>` flag — operator opt-out of Go-binary content probing. C153 `waybill:binary-scan- suppressed` doc-scope annotation across CDX/SPDX 2.3/SPDX 3. * eBPF: - m234 (#682, #686): un-pin bpf-linker to 0.11.0 via pre-built binary install + composite action + daily canary + verify script. * Fixes: - #711: verifier-friendly bound in tls_openssl::probe_ssl_read for kernel bumps. - #680: Go workspace-mode detection in mod_why preflight. - Several release.yml + cosign compat fixes (#674, #675, #681). Golden regeneration: * CDX goldens: 11 files, 1-line churn each — `metadata.tools[].version` bumped 0.2.0 → 0.3.0. * SPDX 2.3 goldens: 11 files. Version-string churn cascades into content-addressed SPDXRef-DocumentRoot-* IDs and documentNamespace URLs (both content-hashed). * SPDX 3 goldens: 11 files. Same content-addressing cascade into doc-/pkg-/anno-/rel- IRIs. Verified: normalized+sorted diff on all 33 goldens shows zero semantic change beyond the version string per project memory `feedback_verify_golden_churn_normalized`. Also updates `waybill-cli/tests/fixtures/pkg_alias_binding/image- baz.cdx.json` (same one-line version bump on an out-of-tree regression fixture). Skipping local pre-PR per project memory `feedback_release_bump_prepr_slow` — version bump invalidates the full compile cache so pre-PR takes 30+ min; CI will verify. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.
Summary
Kernel bump on the `ubuntu-24.04` GHA runner tightened the verifier's scalar-range merge analysis, rejecting the current `if payload_size < 512` pattern in `tls_openssl.rs::probe_ssl_read` with:
Replace the if/else with an unconditional bitmask: `(payload_size & 0x1ff) as usize`. Verifier proves the 0..=511 bound in one pass without needing to widen through a branch merge.
Impact
Bracketing evidence
Test plan
If CI still fails, the fix needs iteration in a Lima VM (macOS host can't run BPF).
🤖 Generated with Claude Code