Skip to content

Commit 37fe5d7

Browse files
r41k0uclaude
andcommitted
Tests: Derive the map-value pointer-depth check from a clang reference
tests/c-form/map_update_from_lookup.bpf.c mirrors the two programs the check covers. At -O0 clang passes bpf_map_update_elem the result of `load ptr, ptr <prev's slot>`, in both copy() and rebind(). The old expected pattern only asked for some load of prev, and `if prev` already does one, so it matched the buggy IR too. The check now requires the value loaded from prev's slot to be the helper's value argument. It fails on the code before dde720f and passes on this branch. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
1 parent f5eb809 commit 37fe5d7

2 files changed

Lines changed: 73 additions & 3 deletions

File tree

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/* Reference for passing a map lookup result to a helper. `prev` is a local
2+
* holding the pointer bpf_map_lookup_elem returned; bpf_map_update_elem gets
3+
* that pointer (the map value), loaded from prev's slot. It never gets &prev,
4+
* the slot's own address, which would make the map store a stack address.
5+
* The clang -O0 IR for this file is the specification for the pointer depth
6+
* that tests/test_signedness_ir.py checks:
7+
* copy() mirrors passing_tests/helpers/map_update_from_lookup.py
8+
* rebind() mirrors passing_tests/vmlinux/named_arg.py, where `prev = prev + 1`
9+
* rebinds prev to a stack temporary holding the sum; the helper
10+
* still gets the pointer loaded from prev's slot. */
11+
#define SEC(name) __attribute__((section(name), used))
12+
#define __uint(name, val) int (*name)[val]
13+
#define __type(name, val) typeof(val) *name
14+
typedef long long __s64;
15+
16+
#define BPF_MAP_TYPE_HASH 1
17+
#define BPF_ANY 0
18+
#define XDP_PASS 2
19+
20+
static void *(*bpf_map_lookup_elem)(void *map, const void *key) = (void *)1;
21+
static long (*bpf_map_update_elem)(void *map, const void *key,
22+
const void *value, __s64 flags) = (void *)2;
23+
24+
struct {
25+
__uint(type, BPF_MAP_TYPE_HASH);
26+
__uint(max_entries, 2);
27+
__type(key, __s64);
28+
__type(value, __s64);
29+
} count SEC(".maps");
30+
31+
SEC("tracepoint/syscalls/sys_enter_getpid")
32+
__s64 copy(void *ctx)
33+
{
34+
__s64 key = 0;
35+
__s64 *prev = bpf_map_lookup_elem(&count, &key);
36+
if (prev) {
37+
key = 1;
38+
bpf_map_update_elem(&count, &key, prev, BPF_ANY);
39+
}
40+
return 0;
41+
}
42+
43+
SEC("xdp")
44+
__s64 rebind(void *ctx)
45+
{
46+
__s64 key = 0;
47+
__s64 tmp;
48+
__s64 *prev = bpf_map_lookup_elem(&count, &key);
49+
if (prev) {
50+
tmp = *prev + 1;
51+
prev = &tmp;
52+
bpf_map_update_elem(&count, &key, prev, BPF_ANY);
53+
return XDP_PASS;
54+
}
55+
tmp = 1;
56+
bpf_map_update_elem(&count, &key, &tmp, BPF_ANY);
57+
return XDP_PASS;
58+
}
59+
60+
char LICENSE[] SEC("license") = "GPL";

‎tests/test_signedness_ir.py‎

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,13 @@
1919
PASSING_DIR = Path(__file__).parent / "passing_tests"
2020
HAVE_VMLINUX = importlib.util.find_spec("vmlinux") is not None
2121

22+
# The value argument of a map update is the very SSA value loaded from prev's
23+
# slot, not merely some load of prev elsewhere in the function.
24+
UPDATE_WITH_LOADED_PREV = (
25+
r'(%"[^"]+") = load i64\*, i64\*\* %"prev"\n(?:.*\n)*?'
26+
r'.*call i64 %"[^"]+"\(\{[^}]*\}\* @"count", i64\* %"[^"]+", i64\* \1, i64 0\)'
27+
)
28+
2229
# path under passing_tests -> (patterns that must appear, patterns that must not)
2330
CASES = {
2431
"signedness/widen_unsigned.py": (
@@ -114,13 +121,16 @@
114121
),
115122
# A local holding a pointer is passed to a helper as that pointer, loaded
116123
# from its slot; the slot's own address (an i64**) would make the map store
117-
# a kernel address instead of the value.
124+
# a kernel address instead of the value. The reference is
125+
# tests/c-form/map_update_from_lookup.bpf.c: at -O0 clang passes
126+
# bpf_map_update_elem the result of `load ptr, ptr <prev's slot>` in both
127+
# copy() and rebind().
118128
"helpers/map_update_from_lookup.py": (
119-
[r'load i64\*, i64\*\* %"prev"'],
129+
[UPDATE_WITH_LOADED_PREV],
120130
[r'i64\*\* %"prev", i64 0\)'],
121131
),
122132
"vmlinux/named_arg.py": (
123-
[r'load i64\*, i64\*\* %"prev"'],
133+
[UPDATE_WITH_LOADED_PREV],
124134
[r'i64\*\* %"prev", i64 0\)'],
125135
),
126136
}

0 commit comments

Comments
 (0)