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>
This commit is contained in:
Amery Hung 2026-05-28 18:49:36 -07:00 committed by Alexei Starovoitov
parent 3f75a757a3
commit 60c7c3b880
3 changed files with 212 additions and 0 deletions

View File

@ -0,0 +1,68 @@
// SPDX-License-Identifier: GPL-2.0
#include <vmlinux.h>
#include "bpf_experimental.h"
#include "bpf_qdisc_common.h"
#include "bpf_misc.h"
char _license[] SEC("license") = "GPL";
int proto;
SEC("struct_ops")
__failure __msg("Expected an initialized dynptr as R1")
int BPF_PROG(invalid_dynptr, struct sk_buff *skb, struct Qdisc *sch,
struct bpf_sk_buff_ptr *to_free)
{
struct bpf_dynptr ptr;
struct ethhdr *hdr;
bpf_dynptr_from_skb((struct __sk_buff *)skb, 0, &ptr);
bpf_qdisc_skb_drop(skb, to_free);
hdr = bpf_dynptr_slice(&ptr, 0, NULL, sizeof(*hdr));
if (!hdr)
return NET_XMIT_DROP;
proto = hdr->h_proto;
return NET_XMIT_DROP;
}
SEC("struct_ops")
__auxiliary
struct sk_buff *BPF_PROG(bpf_qdisc_test_dequeue, struct Qdisc *sch)
{
return NULL;
}
SEC("struct_ops")
__auxiliary
int BPF_PROG(bpf_qdisc_test_init, struct Qdisc *sch, struct nlattr *opt,
struct netlink_ext_ack *extack)
{
return 0;
}
SEC("struct_ops")
__auxiliary
void BPF_PROG(bpf_qdisc_test_reset, struct Qdisc *sch)
{
}
SEC("struct_ops")
__auxiliary
void BPF_PROG(bpf_qdisc_test_destroy, struct Qdisc *sch)
{
}
SEC(".struct_ops")
struct Qdisc_ops test = {
.enqueue = (void *)invalid_dynptr,
.dequeue = (void *)bpf_qdisc_test_dequeue,
.init = (void *)bpf_qdisc_test_init,
.reset = (void *)bpf_qdisc_test_reset,
.destroy = (void *)bpf_qdisc_test_destroy,
.id = "bpf_qdisc_test",
};

View File

@ -0,0 +1,74 @@
// SPDX-License-Identifier: GPL-2.0
#include <vmlinux.h>
#include "bpf_experimental.h"
#include "bpf_qdisc_common.h"
#include "bpf_misc.h"
char _license[] SEC("license") = "GPL";
int proto;
static __noinline int free_skb(struct sk_buff *skb)
{
bpf_kfree_skb(skb);
return 0;
}
SEC("struct_ops")
__failure __msg("invalid mem access 'scalar'")
int BPF_PROG(invalid_dynptr_cross_frame, struct sk_buff *skb, struct Qdisc *sch,
struct bpf_sk_buff_ptr *to_free)
{
struct bpf_dynptr ptr;
struct ethhdr *hdr;
bpf_dynptr_from_skb((struct __sk_buff *)skb, 0, &ptr);
hdr = bpf_dynptr_slice(&ptr, 0, NULL, sizeof(*hdr));
if (!hdr)
return NET_XMIT_DROP;
free_skb(skb);
proto = hdr->h_proto;
return NET_XMIT_DROP;
}
SEC("struct_ops")
__auxiliary
struct sk_buff *BPF_PROG(bpf_qdisc_test_dequeue, struct Qdisc *sch)
{
return NULL;
}
SEC("struct_ops")
__auxiliary
int BPF_PROG(bpf_qdisc_test_init, struct Qdisc *sch, struct nlattr *opt,
struct netlink_ext_ack *extack)
{
return 0;
}
SEC("struct_ops")
__auxiliary
void BPF_PROG(bpf_qdisc_test_reset, struct Qdisc *sch)
{
}
SEC("struct_ops")
__auxiliary
void BPF_PROG(bpf_qdisc_test_destroy, struct Qdisc *sch)
{
}
SEC(".struct_ops")
struct Qdisc_ops test = {
.enqueue = (void *)invalid_dynptr_cross_frame,
.dequeue = (void *)bpf_qdisc_test_dequeue,
.init = (void *)bpf_qdisc_test_init,
.reset = (void *)bpf_qdisc_test_reset,
.destroy = (void *)bpf_qdisc_test_destroy,
.id = "bpf_qdisc_test",
};

View File

@ -0,0 +1,70 @@
// SPDX-License-Identifier: GPL-2.0
#include <vmlinux.h>
#include "bpf_experimental.h"
#include "bpf_qdisc_common.h"
#include "bpf_misc.h"
char _license[] SEC("license") = "GPL";
int proto;
SEC("struct_ops")
__failure __msg("invalid mem access 'scalar'")
int BPF_PROG(invalid_dynptr_slice, struct sk_buff *skb, struct Qdisc *sch,
struct bpf_sk_buff_ptr *to_free)
{
struct bpf_dynptr ptr;
struct ethhdr *hdr;
bpf_dynptr_from_skb((struct __sk_buff *)skb, 0, &ptr);
hdr = bpf_dynptr_slice(&ptr, 0, NULL, sizeof(*hdr));
if (!hdr) {
bpf_qdisc_skb_drop(skb, to_free);
return NET_XMIT_DROP;
}
bpf_qdisc_skb_drop(skb, to_free);
proto = hdr->h_proto;
return NET_XMIT_DROP;
}
SEC("struct_ops")
__auxiliary
struct sk_buff *BPF_PROG(bpf_qdisc_test_dequeue, struct Qdisc *sch)
{
return NULL;
}
SEC("struct_ops")
__auxiliary
int BPF_PROG(bpf_qdisc_test_init, struct Qdisc *sch, struct nlattr *opt,
struct netlink_ext_ack *extack)
{
return 0;
}
SEC("struct_ops")
__auxiliary
void BPF_PROG(bpf_qdisc_test_reset, struct Qdisc *sch)
{
}
SEC("struct_ops")
__auxiliary
void BPF_PROG(bpf_qdisc_test_destroy, struct Qdisc *sch)
{
}
SEC(".struct_ops")
struct Qdisc_ops test = {
.enqueue = (void *)invalid_dynptr_slice,
.dequeue = (void *)bpf_qdisc_test_dequeue,
.init = (void *)bpf_qdisc_test_init,
.reset = (void *)bpf_qdisc_test_reset,
.destroy = (void *)bpf_qdisc_test_destroy,
.id = "bpf_qdisc_test",
};