Skip to content

Commit 60c7c3b

Browse files
ameryhungAlexei Starovoitov
authored andcommitted
selftests/bpf: Test using dynptr after freeing the underlying object
Make sure the verifier invalidates the dynptr and dynptr slice derived from an skb after the skb is freed. Signed-off-by: Amery Hung <ameryhung@gmail.com> Link: https://lore.kernel.org/r/20260529014936.2811085-14-ameryhung@gmail.com Signed-off-by: Alexei Starovoitov <ast@kernel.org>
1 parent 3f75a75 commit 60c7c3b

3 files changed

Lines changed: 212 additions & 0 deletions

File tree

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
#include <vmlinux.h>
4+
#include "bpf_experimental.h"
5+
#include "bpf_qdisc_common.h"
6+
#include "bpf_misc.h"
7+
8+
char _license[] SEC("license") = "GPL";
9+
10+
int proto;
11+
12+
SEC("struct_ops")
13+
__failure __msg("Expected an initialized dynptr as R1")
14+
int BPF_PROG(invalid_dynptr, struct sk_buff *skb, struct Qdisc *sch,
15+
struct bpf_sk_buff_ptr *to_free)
16+
{
17+
struct bpf_dynptr ptr;
18+
struct ethhdr *hdr;
19+
20+
bpf_dynptr_from_skb((struct __sk_buff *)skb, 0, &ptr);
21+
22+
bpf_qdisc_skb_drop(skb, to_free);
23+
24+
hdr = bpf_dynptr_slice(&ptr, 0, NULL, sizeof(*hdr));
25+
if (!hdr)
26+
return NET_XMIT_DROP;
27+
28+
proto = hdr->h_proto;
29+
30+
return NET_XMIT_DROP;
31+
}
32+
33+
SEC("struct_ops")
34+
__auxiliary
35+
struct sk_buff *BPF_PROG(bpf_qdisc_test_dequeue, struct Qdisc *sch)
36+
{
37+
return NULL;
38+
}
39+
40+
SEC("struct_ops")
41+
__auxiliary
42+
int BPF_PROG(bpf_qdisc_test_init, struct Qdisc *sch, struct nlattr *opt,
43+
struct netlink_ext_ack *extack)
44+
{
45+
return 0;
46+
}
47+
48+
SEC("struct_ops")
49+
__auxiliary
50+
void BPF_PROG(bpf_qdisc_test_reset, struct Qdisc *sch)
51+
{
52+
}
53+
54+
SEC("struct_ops")
55+
__auxiliary
56+
void BPF_PROG(bpf_qdisc_test_destroy, struct Qdisc *sch)
57+
{
58+
}
59+
60+
SEC(".struct_ops")
61+
struct Qdisc_ops test = {
62+
.enqueue = (void *)invalid_dynptr,
63+
.dequeue = (void *)bpf_qdisc_test_dequeue,
64+
.init = (void *)bpf_qdisc_test_init,
65+
.reset = (void *)bpf_qdisc_test_reset,
66+
.destroy = (void *)bpf_qdisc_test_destroy,
67+
.id = "bpf_qdisc_test",
68+
};
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
#include <vmlinux.h>
4+
#include "bpf_experimental.h"
5+
#include "bpf_qdisc_common.h"
6+
#include "bpf_misc.h"
7+
8+
char _license[] SEC("license") = "GPL";
9+
10+
int proto;
11+
12+
static __noinline int free_skb(struct sk_buff *skb)
13+
{
14+
bpf_kfree_skb(skb);
15+
return 0;
16+
}
17+
18+
SEC("struct_ops")
19+
__failure __msg("invalid mem access 'scalar'")
20+
int BPF_PROG(invalid_dynptr_cross_frame, struct sk_buff *skb, struct Qdisc *sch,
21+
struct bpf_sk_buff_ptr *to_free)
22+
{
23+
struct bpf_dynptr ptr;
24+
struct ethhdr *hdr;
25+
26+
bpf_dynptr_from_skb((struct __sk_buff *)skb, 0, &ptr);
27+
28+
hdr = bpf_dynptr_slice(&ptr, 0, NULL, sizeof(*hdr));
29+
if (!hdr)
30+
return NET_XMIT_DROP;
31+
32+
free_skb(skb);
33+
34+
proto = hdr->h_proto;
35+
36+
return NET_XMIT_DROP;
37+
}
38+
39+
SEC("struct_ops")
40+
__auxiliary
41+
struct sk_buff *BPF_PROG(bpf_qdisc_test_dequeue, struct Qdisc *sch)
42+
{
43+
return NULL;
44+
}
45+
46+
SEC("struct_ops")
47+
__auxiliary
48+
int BPF_PROG(bpf_qdisc_test_init, struct Qdisc *sch, struct nlattr *opt,
49+
struct netlink_ext_ack *extack)
50+
{
51+
return 0;
52+
}
53+
54+
SEC("struct_ops")
55+
__auxiliary
56+
void BPF_PROG(bpf_qdisc_test_reset, struct Qdisc *sch)
57+
{
58+
}
59+
60+
SEC("struct_ops")
61+
__auxiliary
62+
void BPF_PROG(bpf_qdisc_test_destroy, struct Qdisc *sch)
63+
{
64+
}
65+
66+
SEC(".struct_ops")
67+
struct Qdisc_ops test = {
68+
.enqueue = (void *)invalid_dynptr_cross_frame,
69+
.dequeue = (void *)bpf_qdisc_test_dequeue,
70+
.init = (void *)bpf_qdisc_test_init,
71+
.reset = (void *)bpf_qdisc_test_reset,
72+
.destroy = (void *)bpf_qdisc_test_destroy,
73+
.id = "bpf_qdisc_test",
74+
};
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
#include <vmlinux.h>
4+
#include "bpf_experimental.h"
5+
#include "bpf_qdisc_common.h"
6+
#include "bpf_misc.h"
7+
8+
char _license[] SEC("license") = "GPL";
9+
10+
int proto;
11+
12+
SEC("struct_ops")
13+
__failure __msg("invalid mem access 'scalar'")
14+
int BPF_PROG(invalid_dynptr_slice, struct sk_buff *skb, struct Qdisc *sch,
15+
struct bpf_sk_buff_ptr *to_free)
16+
{
17+
struct bpf_dynptr ptr;
18+
struct ethhdr *hdr;
19+
20+
bpf_dynptr_from_skb((struct __sk_buff *)skb, 0, &ptr);
21+
22+
hdr = bpf_dynptr_slice(&ptr, 0, NULL, sizeof(*hdr));
23+
if (!hdr) {
24+
bpf_qdisc_skb_drop(skb, to_free);
25+
return NET_XMIT_DROP;
26+
}
27+
28+
bpf_qdisc_skb_drop(skb, to_free);
29+
30+
proto = hdr->h_proto;
31+
32+
return NET_XMIT_DROP;
33+
}
34+
35+
SEC("struct_ops")
36+
__auxiliary
37+
struct sk_buff *BPF_PROG(bpf_qdisc_test_dequeue, struct Qdisc *sch)
38+
{
39+
return NULL;
40+
}
41+
42+
SEC("struct_ops")
43+
__auxiliary
44+
int BPF_PROG(bpf_qdisc_test_init, struct Qdisc *sch, struct nlattr *opt,
45+
struct netlink_ext_ack *extack)
46+
{
47+
return 0;
48+
}
49+
50+
SEC("struct_ops")
51+
__auxiliary
52+
void BPF_PROG(bpf_qdisc_test_reset, struct Qdisc *sch)
53+
{
54+
}
55+
56+
SEC("struct_ops")
57+
__auxiliary
58+
void BPF_PROG(bpf_qdisc_test_destroy, struct Qdisc *sch)
59+
{
60+
}
61+
62+
SEC(".struct_ops")
63+
struct Qdisc_ops test = {
64+
.enqueue = (void *)invalid_dynptr_slice,
65+
.dequeue = (void *)bpf_qdisc_test_dequeue,
66+
.init = (void *)bpf_qdisc_test_init,
67+
.reset = (void *)bpf_qdisc_test_reset,
68+
.destroy = (void *)bpf_qdisc_test_destroy,
69+
.id = "bpf_qdisc_test",
70+
};

0 commit comments

Comments
 (0)