Expand description
Types shared between the eBPF program (linprov-ebpf) and the userspace
daemon (linprov). Everything here is repr(C) and Pod-friendly so it
survives a round-trip through a ring buffer and a kernel xattr.
The crate compiles no_std by default (for the BPF target). Enable the
user feature in userspace to pull in bytemuck::Pod / Zeroable
derives on the wire types.
Wire shapes at a glance:
OriginRecordis what the daemon stores in the xattr and in the BPFINODE_MARKSmap. BPF writes most of it infile_open; userspace augmentscreator_pathfrom/proc/$pid/exe.Eventis the ringbuf record streamed from BPF to userspace.AllowRuleis one allowlist rule, packed into the BPFALLOW_RULESarray. String dims are stored asfnv_hashvalues so the BPF side can compare without carrying full byte arrays.
use linprov_common::{fnv_hash, dim};
// Both sides hash strings the same way; same input → same u64.
assert_eq!(fnv_hash("/usr/bin/curl"), fnv_hash("/usr/bin/curl"));
assert_ne!(fnv_hash("/usr/bin/curl"), fnv_hash("/usr/bin/wget"));
// Dimension bits are independent flags on AllowRule::flags.
let two_dim = dim::CREATOR_UID | dim::CREATOR_COMM;
assert_eq!(two_dim.count_ones(), 2);Modules§
- dim
flagsbits onAllowRule. Bits 0–7 are the dims a rule requires. Bits 8+ are modifiers on a dim already set.
Structs§
- Allow
Rule - One allowlist rule. Set bits in
flagsmark required dims; the corresponding fields below are then compared against the record / execve context at enforce time. Cleared bits → field ignored. - Event
- Ring-buffer record. Two kinds:
NetworkFileOpen — informational; eBPF just wrote (or tried to write)
the xattr.
statusis the kfunc return code. Execve — bprm_check fired AND the file already carried the mark.originis the record we read back;statusis unused. - Origin
Record - Provenance record. Carried in the
security.bpf.linprov.originxattr and in the INODE_MARKS storage map. Fixed 64 bytes — every variable-length field is an FNV-1a-64 hash, so the record never grows with path length and always fits a single xattr block.
Constants§
- COMM_
LEN - EVENT_
KIND_ DERIVED_ FILE_ OPEN - A file written by a process that had read a marked file (taint
propagation — e.g.
tar/unzipextracting a marked archive, orcpof a marked file). The carriedoriginis inherited from the source file’s record; userspace persists the xattr without re-resolving the creator (the inherited creator identity is kept verbatim). - EVENT_
KIND_ EXECVE - EVENT_
KIND_ NETWORK_ FILE_ OPEN - EVENT_
KIND_ SCRIPT_ EXEC - A marked file opened for read by a known script interpreter
(bash/python/…) — i.e.
python foo.py/bash foo.sh/. foo.sh, where the kernel execve’s the unmarked interpreter and the script itself never reachesbprm_check_security. The eBPFfile_openread branch runs the same allowlist check used at execve against the script’s path; in enforce modestatusis the LSM verdict (-1blocked). Thefilenamecarries the script’s path andcommthe script’s basename, so the script — not the interpreter — is the unit surfaced in logs and soak. MatchesEVENT_KIND_EXECVEhandling. - EXEC_
PATH_ LEN - Live exec/target path buffer size: the ringbuf
Eventfilename and the per-CPU scratch the target-dim walks scan. Sized to LinuxPATH_MAXsotarget_filename/target_foldermatch the full execution path at any depth and any length. These buffers are transient (per-CPU scratch + ringbuf), never persisted, so they aren’t bound by the xattr block-size limit that caps stored data. - FNV_
OFFSET - FNV_
PRIME - MAX_
FOLDER_ ANCESTORS - Number of landing-folder ancestor hashes stored per record, for
nested
landing_foldermatching. The walk records the hash of each/-terminated prefix of the landing path (shallow → deep) into a[u64; MAX_FOLDER_ANCESTORS]; a rule matches if its folder hash equals any of them, solanding_folder=/home/user/matches a file that landed in/home/user/Downloads/sub/. Bounds nesting depth (path length is still unbounded — these are hashes). Must be a power of two: the in-kernel walk masks the index (& (N-1)) to keep the array write provably in-bounds without a panic branch. Real landing paths sit well under this, so the mask never actually wraps. - MAX_
RULES - Maximum number of allowlist rules carried by the BPF Array map. Each rule check is ~30 ops + 2 folder lookups; the verifier walks the full bounded loop, so this caps the per-execve cost.
- MODE_
ENFORCE - MODE_
OBSERVE - Runtime mode communicated to the eBPF program via the CONFIG map.
- MODE_
SOAK - ORIGIN_
VERSION - Current schema version of
OriginRecord. Records carrying a different version are treated as unmarked. - PATH_
HASH_ SCAN_ LEN - Max bytes the BPF path walks inspect. Equal to
EXEC_PATH_LEN: the walk body is abpf_loopcallback the verifier inspects once, so this is bounded only by the buffer, not the instruction budget. - XATTR_
NAME
Functions§
- fnv_
hash - Hash a string with FNV-1a-64. Byte-by-byte, no trailing NUL, no padding — identical on the BPF and userspace sides.
- fnv_
hash_ bytes - Same as
fnv_hash, but takes a byte slice. Useful when the source isn’t UTF-8 (e.g., a[u8; EXEC_PATH_LEN]filename buffer read out of a ringbuf event).