Skip to content

Commit db89408

Browse files
r41k0uclaude
andcommitted
Tests: Packet pointers through locals, bounds checks, data_meta, and the refusals
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
1 parent b9b582a commit db89408

8 files changed

Lines changed: 146 additions & 0 deletions

File tree

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# data_end can only be compared against; the verifier rejects arithmetic on
2+
# it, so the compiler refuses it.
3+
from ctypes import c_int64, c_uint32 # noqa: F401
4+
from pythonbpf import bpf, section, bpfglobal, compile
5+
from vmlinux import struct_xdp_md
6+
7+
8+
@bpf
9+
@section("xdp")
10+
def prog(ctx: struct_xdp_md) -> c_int64:
11+
e = ctx.data_end + 1
12+
return c_int64(e)
13+
14+
15+
@bpf
16+
@bpfglobal
17+
def LICENSE() -> str:
18+
return "GPL"
19+
20+
21+
compile()
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# A packet pointer stored into a 32-bit slot would lose it; the verifier only
2+
# accepts packet pointers at 64 bits, so the compiler refuses the narrowing.
3+
from ctypes import c_int64, c_uint32 # noqa: F401
4+
from pythonbpf import bpf, section, bpfglobal, compile
5+
from vmlinux import struct_xdp_md
6+
7+
8+
@bpf
9+
@section("xdp")
10+
def prog(ctx: struct_xdp_md) -> c_int64:
11+
x = c_uint32(0)
12+
x = ctx.data
13+
return c_int64(x)
14+
15+
16+
@bpf
17+
@bpfglobal
18+
def LICENSE() -> str:
19+
return "GPL"
20+
21+
22+
compile()
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# The canonical XDP bounds check, through locals: the packet-pointer type
2+
# travels with data and data_end, so data + 34 > data_end is a 64-bit
3+
# unsigned comparison, which is what the verifier needs to prove the range.
4+
from ctypes import c_int64, c_uint32 # noqa: F401
5+
from pythonbpf import bpf, section, bpfglobal, compile
6+
from vmlinux import struct_xdp_md
7+
8+
9+
@bpf
10+
@section("xdp")
11+
def prog(ctx: struct_xdp_md) -> c_int64:
12+
data = ctx.data
13+
data_end = ctx.data_end
14+
if data + 34 > data_end:
15+
return c_int64(1)
16+
return c_int64(2)
17+
18+
19+
@bpf
20+
@bpfglobal
21+
def LICENSE() -> str:
22+
return "GPL"
23+
24+
25+
compile()
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# The same bounds check written on the fields directly.
2+
from ctypes import c_int64, c_uint32 # noqa: F401
3+
from pythonbpf import bpf, section, bpfglobal, compile
4+
from vmlinux import struct_xdp_md
5+
6+
7+
@bpf
8+
@section("xdp")
9+
def prog(ctx: struct_xdp_md) -> c_int64:
10+
if ctx.data + 14 > ctx.data_end:
11+
return c_int64(1)
12+
return c_int64(2)
13+
14+
15+
@bpf
16+
@bpfglobal
17+
def LICENSE() -> str:
18+
return "GPL"
19+
20+
21+
compile()
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# data_meta is a packet-metadata pointer; offsets are allowed on it too.
2+
from ctypes import c_int64, c_uint32 # noqa: F401
3+
from pythonbpf import bpf, section, bpfglobal, compile
4+
from vmlinux import struct_xdp_md
5+
6+
7+
@bpf
8+
@section("xdp")
9+
def prog(ctx: struct_xdp_md) -> c_int64:
10+
m = ctx.data_meta + 4
11+
return c_int64(m)
12+
13+
14+
@bpf
15+
@bpfglobal
16+
def LICENSE() -> str:
17+
return "GPL"
18+
19+
20+
compile()
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# A local copied from a packet-pointer field is still a packet pointer: its
2+
# slot is 64-bit and d + 14 is 64-bit pointer arithmetic.
3+
from ctypes import c_int64, c_uint32 # noqa: F401
4+
from pythonbpf import bpf, section, bpfglobal, compile
5+
from vmlinux import struct_xdp_md
6+
7+
8+
@bpf
9+
@section("xdp")
10+
def prog(ctx: struct_xdp_md) -> c_int64:
11+
data = ctx.data
12+
eth = data + 14
13+
return c_int64(eth)
14+
15+
16+
@bpf
17+
@bpfglobal
18+
def LICENSE() -> str:
19+
return "GPL"
20+
21+
22+
compile()

‎tests/test_config.toml‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,3 +64,6 @@
6464

6565
# A packet offset has to fit the verifier's 16-bit packet range.
6666
"failing_tests/vmlinux/ctx_data_offset_too_big.py" = {reason = "A constant packet offset outside 0..65535 is a compile error (the verifier tracks packet ranges up to MAX_PACKET_OFF)", level = "ir"}
67+
"failing_tests/vmlinux/ctx_data_end_offset.py" = {reason = "A packet-end pointer can only be compared, not offset (the verifier rejects arithmetic on it)", level = "ir"}
68+
69+
"failing_tests/vmlinux/ctx_data_narrow_store.py" = {reason = "A packet pointer cannot be narrowed below 64 bits", level = "ir"}

‎tests/test_signedness_ir.py‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,18 @@
8888
[r"\bsub i64"],
8989
[r"to i16", r"trunc i64 %.* to i32"],
9090
),
91+
"vmlinux/ctx_bounds_check.py": (
92+
[r"icmp ugt i64", r"\badd i64"],
93+
[r"trunc i64 %.* to i32", r"icmp \w+ i32"],
94+
),
95+
"vmlinux/ctx_bounds_check_direct.py": (
96+
[r"icmp ugt i64"],
97+
[r"trunc i64 %.* to i32", r"icmp \w+ i32"],
98+
),
99+
"vmlinux/ctx_data_via_local.py": (
100+
[r"\badd i64"],
101+
[r"trunc i64 %.* to i32"],
102+
),
91103
# A bool widens with zext and an integer narrows to it by != 0, never by
92104
# trunc; sext of an i1 would return -1 for True.
93105
"signedness/bool_int.py": (

0 commit comments

Comments
 (0)