Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ This file records each release. The format follows [Keep a Changelog](https://ke

## Unreleased

### Added

- The eBPF verifier gives the result of kernel/bpf/verifier.c of Linux for all programs of bpf/test_verifier of the socket filter and cgroup program types. It prunes the paths with equal states at the jump targets and refuses a loop that does not end, it refines the bounds of a register after `BPF_JSET` and after a `BPF_JMP32` that compares as 64 bits, and it checks the alignment of the pointers and the size of each context field as Linux, with the errors and the log messages of Linux in the same order. `bpf(BPF_PROG_TEST_RUN)` runs a program on a packet or a context from user space. The kselftest boot test runs bpf/test_verifier, and tools/kselftest/bpf.md gives the result: 281 passed and 0 failed (#537).

## [0.5.22] - 2026-11-12

### Added
Expand Down
3 changes: 3 additions & 0 deletions core/rk-abi/src/errno.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ impl Errno {
/// The buffer is too small, as `ETOOSMALL` of include/linux/errno.h. Linux gives it to user
/// space from `ETHTOOL_GPERMADDR`, so user space sees this number.
pub const ETOOSMALL: Self = Self(525);
/// The operation is not supported, as `ENOTSUPP` of include/linux/errno.h. `bpf(2)` gives it
/// to user space, for example from `BPF_PROG_TEST_RUN` of a program type without a test run.
pub const ENOTSUPP: Self = Self(524);

/// Tells if the number is one of the restart numbers of the kernel.
pub const fn is_restart(self) -> bool {
Expand Down
2 changes: 2 additions & 0 deletions core/rk-bpf/src/attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ pub mod last_field {
pub const BPF_PROG_ATTACH: usize = 32;
/// `BPF_PROG_DETACH`: `expected_revision`.
pub const BPF_PROG_DETACH: usize = 32;
/// `BPF_PROG_TEST_RUN`: `test.batch_size`.
pub const BPF_PROG_TEST_RUN: usize = 76;
/// `BPF_PROG_QUERY`: `query.revision`.
pub const BPF_PROG_QUERY: usize = 64;
/// `BPF_LINK_CREATE`: `link_create.uprobe_multi.path_fd`.
Expand Down
4 changes: 2 additions & 2 deletions core/rk-bpf/src/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -668,7 +668,7 @@ impl Run<'_> {
}

#[cfg(test)]
mod tests {
pub(crate) mod tests {
use super::*;
use crate::insn::asm::*;
use crate::map::MapEnv;
Expand All @@ -694,7 +694,7 @@ mod tests {
}
}

fn prog(insns: &[BpfInsn], maps: Vec<Arc<BpfMap>>) -> BpfProg {
pub(crate) fn prog(insns: &[BpfInsn], maps: Vec<Arc<BpfMap>>) -> BpfProg {
BpfProg {
prog_type: 1,
expected_attach_type: 0,
Expand Down
1 change: 1 addition & 0 deletions core/rk-bpf/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ pub mod link;
pub mod map;
pub mod prog;
pub mod syscall;
pub mod test_run;
pub mod tnum;
pub mod uapi;
pub mod value;
Expand Down
26 changes: 24 additions & 2 deletions core/rk-bpf/src/prog.rs
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,14 @@ pub struct SkBuff<'a> {
pub remote_port: u32,
/// The local port, in host byte order.
pub local_port: u32,
/// `skb->tstamp`, the time of the packet in nanoseconds.
pub tstamp: u64,
/// `gso_segs` of the shared info of the packet.
pub gso_segs: u32,
/// `gso_size` of the shared info of the packet.
pub gso_size: u32,
/// The time stamp of the hardware, in nanoseconds.
pub hwtstamp: u64,
}

/// The offsets in `struct bpf_sock`, the context of `BPF_PROG_TYPE_CGROUP_SOCK`.
Expand Down Expand Up @@ -509,8 +517,15 @@ pub mod sk_buff {
pub const LOCAL_IP6: u32 = 116;
pub const REMOTE_PORT: u32 = 132;
pub const LOCAL_PORT: u32 = 136;
pub const DATA_META: u32 = 140;
pub const FLOW_KEYS: u32 = 144;
pub const TSTAMP: u32 = 152;
pub const WIRE_LEN: u32 = 160;
pub const GSO_SEGS: u32 = 164;
pub const SK: u32 = 168;
pub const GSO_SIZE: u32 = 176;
pub const TSTAMP_TYPE: u32 = 180;
pub const HWTSTAMP: u32 = 184;
/// `sizeof(struct __sk_buff)`.
pub const SIZE: u32 = 192;
}
Expand All @@ -525,6 +540,11 @@ impl ProgCtx for SkBuff<'_> {
v[..b.len()].copy_from_slice(b);
return Some(u64::from_le_bytes(v));
}
match off {
TSTAMP => return Some(self.tstamp),
HWTSTAMP => return Some(self.hwtstamp),
_ => {}
}
// A narrow load reads the low bytes of the 32-bit field, as `bpf_ctx_narrow_access_ok`.
let field = off & !3;
let v: u32 = match field {
Expand All @@ -549,8 +569,9 @@ impl ProgCtx for SkBuff<'_> {
let at = (f - LOCAL_IP6) as usize;
u32::from_ne_bytes(self.local_ip[at..at + 4].try_into().ok()?)
}
QUEUE_MAPPING | VLAN_PRESENT | VLAN_TCI | VLAN_PROTO | TC_INDEX | NAPI_ID
| GSO_SEGS | GSO_SIZE => 0,
GSO_SEGS => self.gso_segs,
GSO_SIZE => self.gso_size,
QUEUE_MAPPING | VLAN_PRESENT | VLAN_TCI | VLAN_PROTO | TC_INDEX | NAPI_ID => 0,
_ => return None,
};
let shift = (off - field) * 8;
Expand All @@ -563,6 +584,7 @@ impl ProgCtx for SkBuff<'_> {
match off {
MARK => self.mark = value as u32,
PRIORITY => self.priority = value as u32,
TSTAMP => self.tstamp = value,
_ if (CB..CB_END).contains(&off) => {}
_ => return None,
}
Expand Down
44 changes: 44 additions & 0 deletions core/rk-bpf/src/syscall.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ pub trait Env {
fn boot_ns(&self) -> u64;
/// The real user ID of the caller.
fn uid(&self) -> u32;
/// What the helpers of a program see when the call runs it: the task of the caller.
fn helpers(&self) -> &dyn crate::helpers::HelperEnv;
/// The cgroup of the cgroup2 directory `fd`, `cgroup_get_from_fd` of Linux. Another file
/// gives `EBADF`.
fn cgroup_get_from_fd(&self, fd: u32) -> Result<CgId>;
Expand Down Expand Up @@ -192,6 +194,7 @@ pub fn sys_bpf(
BPF_PROG_ATTACH => bpf_prog_attach(env, &attr),
BPF_PROG_DETACH => bpf_prog_detach(env, &attr),
BPF_PROG_QUERY => bpf_prog_query(env, &attr, uattr, size),
BPF_PROG_TEST_RUN => bpf_prog_test_run(env, &attr, uattr),
BPF_LINK_CREATE => link_create_cmd(env, &attr),
BPF_LINK_UPDATE => link_update_cmd(env, &attr),
BPF_LINK_DETACH => link_detach(env, &attr),
Expand Down Expand Up @@ -400,6 +403,8 @@ fn bpf_prog_load(env: &dyn Env, attr: &BpfAttr, log_attr: &BpfLogAttr) -> Result
expected_attach_type,
bpf_capable: bpf_cap,
allow_ptr_leaks: env.capable(CAP_PERFMON) || env.capable(CAP_SYS_ADMIN),
strict_alignment: prog_flags & BPF_F_STRICT_ALIGNMENT != 0
&& prog_flags & BPF_F_ANY_ALIGNMENT == 0,
};
let map_by_fd = |fd: i32| -> Result<Arc<BpfMap>> {
let file = env.fdget(fd)?;
Expand Down Expand Up @@ -1140,6 +1145,41 @@ fn bpf_obj_get_info_by_fd(env: &dyn Env, attr: &BpfAttr, uattr: u64) -> Result<i
Err(Errno::EINVAL)
}

/// The test run of a program, `bpf_prog_test_run` of Linux.
fn bpf_prog_test_run(env: &dyn Env, attr: &BpfAttr, uattr: u64) -> Result<i64> {
use crate::test_run::test;
if attr.check_attr(last_field::BPF_PROG_TEST_RUN) {
return Err(Errno::EINVAL);
}
let both = |size: usize, ptr: usize| (attr.u32(size) == 0) == (attr.u64(ptr) == 0);
if !both(test::CTX_SIZE_IN, test::CTX_IN) || !both(test::CTX_SIZE_OUT, test::CTX_OUT) {
return Err(Errno::EINVAL);
}
let prog = bpf_prog_get(env, attr.u32(test::PROG_FD))?;
crate::test_run::bpf_prog_test_run(&EnvTestRun(env), &prog, attr, uattr)
}

/// The caller of a test run.
struct EnvTestRun<'a>(&'a dyn Env);

impl crate::test_run::TestRunEnv for EnvTestRun<'_> {
fn copy_from_user(&self, addr: u64, buf: &mut [u8]) -> Result<()> {
self.0.copy_from_user(addr, buf)
}

fn copy_to_user(&self, addr: u64, buf: &[u8]) -> Result<()> {
self.0.copy_to_user(addr, buf)
}

fn helpers(&self) -> &dyn crate::helpers::HelperEnv {
self.0.helpers()
}

fn now_ns(&self) -> u64 {
self.0.boot_ns()
}
}

/// The program of a descriptor, `bpf_prog_get` of Linux.
fn bpf_prog_get(env: &dyn Env, fd: u32) -> Result<Arc<BpfProg>> {
bpf_prog_of(&*env.fdget(fd as i32)?).ok_or(Errno::EINVAL)
Expand Down Expand Up @@ -1592,6 +1632,10 @@ mod tests {
1000
}

fn helpers(&self) -> &dyn crate::helpers::HelperEnv {
&crate::core::tests::NoEnv
}

// The descriptors from 5000 are the cgroups 5000 and up, each one the child of the
// one before.
fn cgroup_get_from_fd(&self, fd: u32) -> Result<CgId> {
Expand Down
Loading
Loading