https://github.com/tonywu6/ebpf-packet-first-byte
https://github.com/tonywu6/ebpf-packet-first-byte
ebpf ebpf-rust
Last synced: about 1 month ago
JSON representation
- Host: GitHub
- URL: https://github.com/tonywu6/ebpf-packet-first-byte
- Owner: tonywu6
- License: apache-2.0
- Created: 2025-09-24T05:38:12.000Z (9 months ago)
- Default Branch: main
- Last Pushed: 2025-09-24T05:38:30.000Z (9 months ago)
- Last Synced: 2025-11-12T03:27:04.992Z (7 months ago)
- Topics: ebpf, ebpf-rust
- Language: Rust
- Homepage:
- Size: 20.5 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE-APACHE
Awesome Lists containing this project
README
# ebpf-packet-first-byte
A corner case with the eBPF verifier when trying to load exactly the first byte of
packet data via [direct packet access], using [Aya].
## Problem
The eBPF verifier requires programs to check that pointers are within the range
`skb.data..skb.data_end` before accessing packet data.
[The following logic](example1/src/main.rs#L24-35) is commonly used:
```rs
fn ptr_at(skb: &SkBuff, offset: usize) -> Result<*const T, ()> {
let skb = unsafe { &*skb.skb };
let ptr = skb.data as usize;
let ptr = ptr + offset;
let end = skb.data_end as usize;
if ptr + size_of::() > end {
Err(())
} else {
Ok(ptr as _)
}
}
```
However, loading from a pointer returned by `ptr_at::(skb, 0)` (loading only the
first byte) apparently fails the verification:
```
the BPF_PROG_LOAD syscall failed. Verifier output: 0: R1=ctx() R10=fp0
0: (61) r2 = *(u32 *)(r1 +76) ; R1=ctx() R2_w=pkt(r=0)
1: (61) r1 = *(u32 *)(r1 +80) ; R1_w=pkt_end()
2: (3d) if r2 >= r1 goto pc+11 ; R1_w=pkt_end() R2_w=pkt(r=0)
3: (71) r6 = *(u8 *)(r2 +0)
invalid access to packet, off=0 size=1, R2(id=0,off=0,r=0)
R2 offset is outside of the packet
```
This does not happen when loading more than 1 byte or loading not from the first byte
(such as `ptr_at::<[u8; 2]>(skb, 0)` or `ptr_at::(skb, 1)`).
Comparing verifier outputs:
```diff
- ; ptr_at::(skb, 0)
- 2: (3d) if r2 >= r1 goto pc+11 ; R1_w=pkt_end() R2_w=pkt(r=0)
+ ; ptr_at::(skb, 1)
+ 3: (07) r3 += 2 ; R3_w=pkt(off=2,r=0)
+ 4: (2d) if r3 > r2 goto pc+8 ; R2_w=pkt_end() R3_w=pkt(off=2,r=2)
```
it appears that the compiler is optimizing the comparison
`skb.data + 0 + 1 > skb.data_end` into `skb.data >= skb.data_end`, which results in the
verifier not updating known packet length (`pkt(r=0)` indicates 0 bytes starting from
`skb.data` is safe to read).
## Solution
[Rewriting](example2/src/main.rs#L24-35) `ptr_at` to use **pointer arithmetic** instead
of integer arithmetic causes the correct instruction to be generated:
```rs
#[inline]
fn ptr_at(skb: &SkBuff, offset: usize) -> Result<*const T, ()> {
let skb = unsafe { &*skb.skb };
let ptr = skb.data as *const u8;
let ptr = unsafe { ptr.add(offset) };
let end = skb.data_end as *const u8;
if unsafe { ptr.add(size_of::()) } > end {
Err(())
} else {
Ok(ptr as _)
}
}
```
```diff
0: (61) r2 = *(u32 *)(r1 +80) ; R1=ctx() R2_w=pkt_end()
1: (61) r1 = *(u32 *)(r1 +76) ; R1_w=pkt(r=0)
2: (bf) r3 = r1 ; R1_w=pkt(r=0) R3_w=pkt(r=0)
+ 3: (07) r3 += 1 ; R3_w=pkt(off=1,r=0)
+ 4: (2d) if r3 > r2 goto pc+8 ; R2_w=pkt_end() R3_w=pkt(off=1,r=1)
```
## Environment
- Linux 6.15.11 ([`mcr.microsoft.com/devcontainers/rust:bookworm`][bookworm])
- LLVM 21.1.1
## License
With the exception of eBPF code, ebpf-packet-first-byte is distributed under the terms
of either the [MIT license][mit] or the [Apache License][apache] (version 2.0), at your
option.
Unless you explicitly state otherwise, any contribution intentionally submitted for
inclusion in this crate by you, as defined in the Apache-2.0 license, shall be dual
licensed as above, without any additional terms or conditions.
### eBPF
All eBPF code is distributed under either the terms of the [GNU General Public License,
Version 2][gplv2] or the [MIT license][mit], at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for
inclusion in this project by you, as defined in the GPL-2 license, shall be dual
licensed as above, without any additional terms or conditions.
[apache]: LICENSE-APACHE
[Aya]: https://aya-rs.dev
[bookworm]: https://github.com/devcontainers/images/blob/main/src/rust/README.md
[direct packet access]: https://docs.kernel.org/bpf/verifier.html#direct-packet-access
[gplv2]: LICENSE-GPL2
[mit]: LICENSE-MIT