Validator (C/libbpf) Behavior
June 11, 2026 · View on GitHub
Purpose
validator/c-libbpf/bin/bpfcompat-validator is executed inside each VM target and performs real kernel-facing validation steps for one artifact.
CLI inputs
--artifact <path>(required)--out <result.json>(required)--manifest <path>(optional)--functional-plan <path>(optional, generated from manifestfunctional_tests)--log-dir <dir>(optional)--attach-mode <disabled|best-effort|required>(optional, defaultbest-effort)--probe-features <bool>(optional, defaulttrue)--set-map-max-entries <map>=<n|cpus>(repeatable, generated from manifestmaps)--set-map-inner-ringbuf <map>=<bytes>(repeatable, generated from manifestmaps)--prog-variants <group>=<prog>:<helper_id>,...(repeatable, generated from manifestprogram_variants)
Map fixups for runtime-sized maps
Some artifacts compile maps with max_entries=0 and rely on their userspace
loader to size them before load — per-CPU arrays and ring buffers sized from
the CPU count are the common cases (Falco's modern_bpf probe does both).
Under a generic loader such objects fail with EINVAL on every kernel, which
is a loader-contract issue, not a kernel-compatibility signal.
Declaring the maps in the manifest makes the validator mirror the real
loader between bpf_object open and load:
maps:
- name: auxiliary_maps
max_entries: cpus # or a positive integer
- name: ringbuf_maps
max_entries: cpus
inner_ringbuf_bytes: 8388608
max_entries: cpusresolves tolibbpf_num_possible_cpus()on the target kernel at load time.inner_ringbuf_bytescreates aBPF_MAP_TYPE_RINGBUFof that size and installs it as the inner-map prototype for an array-of-maps (bpf_map__set_inner_map_fd).
Fixups apply to the whole-object load and to isolated per-program load
probes. Per-fixup outcomes are emitted in the result JSON under
map_fixups and surfaced as target notes in the report.
Program variant groups
Some artifacts ship multiple programs for the same event and let their
loader pick one by probing kernel helper support — Falco's loader selects
recvmmsg_x (requires bpf_loop, kernel >= 5.17) or falls back to
recvmmsg_old_x, disabling the loser before load. The losing variant is
expected to fail verification on kernels missing its helper, so loading
it is a loader-contract violation, not kernel-compatibility evidence.
Declare the groups in the manifest; the validator mirrors the selection between open and load:
program_variants:
- group: recvmmsg_x
programs:
- name: recvmmsg_x
requires_helper: bpf_loop # known helper name or numeric id
- name: recvmmsg_old_x # unconditional fallback
Variant order is selection priority. Helper support is probed with
libbpf_probe_bpf_helper (raw-tracepoint program type, matching Falco's
libpman). The chosen/disabled variants per group are emitted in the result
JSON under program_variants and surfaced as target notes — so the
matrix records not just pass/fail but which variant a kernel gets.
Isolated per-program load probes are unaffected: they still report each
variant's own load result on the kernel, which is exactly the per-variant
evidence the selection is based on. Objects that statically initialize
prog-array (tail-call) slots cannot be loaded one program at a time;
those per-program probes are reported as skipped rather than fail.
Execution phases
- Capture host metadata (
uname, timestamp). - Capture BTF metadata:
- kernel BTF presence/size
- artifact
.BTF/.BTF.extpresence
- Capability probing:
bpftool feature probecapture when available- custom map/program probe fallback
- attach prerequisite checks
- Open BPF object with libbpf.
- Discover maps/program sections and initialize per-program attach/load state.
- When feature probing is enabled, run isolated per-program load probes and capture bounded verifier logs.
- Attempt whole-object load (
bpf_object__load). - Attempt auto-attach for eligible sections based on attach mode.
- If a functional plan is supplied, keep successful BPF links alive and run project-specific functional commands.
- Emit JSON result and optional libbpf log file.
Output contract
Primary output is a JSON document (currently schema_version: validator.v0.4) containing:
status(pass/fail)hostmetadatainputsettings (artifact path, attach mode, probe mode)btfdetailscapabilitiesdetails (bpftool + custom probes)discoverydetails (program/map counts and per-program attach status)- per-program isolated load status (
load_status,load_errno,load_log) when feature probing is enabled loadstatus (pass/fail, error code/message)attachaggregate status and countersfunctionalaggregate status and per-command result detailslogs.libbpfcaptured log stream
Functional tests
Functional tests are declared in the manifest and converted by the Go runner into a strict validator plan. They are executed inside the VM while successful libbpf links are still alive. This lets a project supply a command such as a smoke script or event-capture harness that proves more than "the object loaded."
Example:
functional_tests:
- name: execve-stimulus-smoke
command: "sh -c 'printf bpfcompat-functional-smoke'"
timeout: 5s
expect_exit_code: 0
expect_stdout_contains: bpfcompat-functional-smoke
A required functional-test failure marks the target as FUNCTIONAL_TEST_FAILURE.
The examples/functional-execve fixture is the concrete event-capture example:
it attaches to sys_enter_execve, triggers /bin/true while the BPF link is
alive, and requires the expected marker to appear in trace_pipe.
Attach behavior
disabled: no attach attempts.best-effort: attach failures do not fail overall status if load passed.required: attach failures fail overall validator status.
Failure evidence used upstream
Host-side classification consumes:
- load status/error code/message
- attach status/mode
- BTF presence signals
- libbpf/verifier text
- per-program isolated load failures and verifier log tails
- functional test status/output when supplied
This supports deterministic classification into codes such as:
UNSUPPORTED_ATTACH_TYPE, UNSUPPORTED_MAP_TYPE, UNSUPPORTED_PROGRAM_TYPE,
MISSING_BTF, CORE_RELOCATION_FAILURE, POLICY_DENIED,
UNSUPPORTED_TRANSPORT, and others.