Skip to content

Commit 30190f8

Browse files
author
Martin KaFai Lau
committed
Merge branch 'fix-bpf-qdisc-bugs-and-clean-up'
Amery Hung says: ==================== Fix bpf qdisc bugs and clean up This patchset fixes the following bugs in bpf qdisc and clean up the selftest. - A null-pointer dereference can happen in qdisc_watchdog_cancel() if the timer is not initialized when 1) .init is not defined by user so init prologue is not generated. 2) .init fails and qdisc_create() calls .destroy - bpf qdisc fails to attach to mq/mqprio when being set as the default qdisc due to failed qdisc_lookup() in init prologue v2 - Rebase to bpf-next/net - Fix erroneous commit messages - Fix and simplify selftests cleanup v1: https://lore.kernel.org/bpf/[email protected]/ ==================== Link: https://patch.msgid.link/[email protected] Signed-off-by: Martin KaFai Lau <[email protected]>
2 parents 1b1f563 + 2f9838e commit 30190f8

File tree

6 files changed

+160
-45
lines changed

6 files changed

+160
-45
lines changed

net/sched/bpf_qdisc.c

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -234,18 +234,20 @@ __bpf_kfunc int bpf_qdisc_init_prologue(struct Qdisc *sch,
234234
struct net_device *dev = qdisc_dev(sch);
235235
struct Qdisc *p;
236236

237+
qdisc_watchdog_init(&q->watchdog, sch);
238+
237239
if (sch->parent != TC_H_ROOT) {
240+
/* If qdisc_lookup() returns NULL, it means .init is called by
241+
* qdisc_create_dflt() in mq/mqprio_init and the parent qdisc
242+
* has not been added to qdisc_hash yet.
243+
*/
238244
p = qdisc_lookup(dev, TC_H_MAJ(sch->parent));
239-
if (!p)
240-
return -ENOENT;
241-
242-
if (!(p->flags & TCQ_F_MQROOT)) {
245+
if (p && !(p->flags & TCQ_F_MQROOT)) {
243246
NL_SET_ERR_MSG(extack, "BPF qdisc only supported on root or mq");
244247
return -EINVAL;
245248
}
246249
}
247250

248-
qdisc_watchdog_init(&q->watchdog, sch);
249251
return 0;
250252
}
251253

@@ -393,6 +395,17 @@ static void bpf_qdisc_unreg(void *kdata, struct bpf_link *link)
393395
return unregister_qdisc(kdata);
394396
}
395397

398+
static int bpf_qdisc_validate(void *kdata)
399+
{
400+
struct Qdisc_ops *ops = (struct Qdisc_ops *)kdata;
401+
402+
if (!ops->enqueue || !ops->dequeue || !ops->init ||
403+
!ops->reset || !ops->destroy)
404+
return -EINVAL;
405+
406+
return 0;
407+
}
408+
396409
static int Qdisc_ops__enqueue(struct sk_buff *skb__ref, struct Qdisc *sch,
397410
struct sk_buff **to_free)
398411
{
@@ -430,6 +443,7 @@ static struct bpf_struct_ops bpf_Qdisc_ops = {
430443
.verifier_ops = &bpf_qdisc_verifier_ops,
431444
.reg = bpf_qdisc_reg,
432445
.unreg = bpf_qdisc_unreg,
446+
.validate = bpf_qdisc_validate,
433447
.init_member = bpf_qdisc_init_member,
434448
.init = bpf_qdisc_init,
435449
.name = "Qdisc_ops",

tools/testing/selftests/bpf/prog_tests/bpf_qdisc.c

Lines changed: 85 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include "network_helpers.h"
88
#include "bpf_qdisc_fifo.skel.h"
99
#include "bpf_qdisc_fq.skel.h"
10+
#include "bpf_qdisc_fail__incompl_ops.skel.h"
1011

1112
#define LO_IFINDEX 1
1213

@@ -49,42 +50,32 @@ static void do_test(char *qdisc)
4950
static void test_fifo(void)
5051
{
5152
struct bpf_qdisc_fifo *fifo_skel;
52-
struct bpf_link *link;
5353

5454
fifo_skel = bpf_qdisc_fifo__open_and_load();
5555
if (!ASSERT_OK_PTR(fifo_skel, "bpf_qdisc_fifo__open_and_load"))
5656
return;
5757

58-
link = bpf_map__attach_struct_ops(fifo_skel->maps.fifo);
59-
if (!ASSERT_OK_PTR(link, "bpf_map__attach_struct_ops")) {
60-
bpf_qdisc_fifo__destroy(fifo_skel);
61-
return;
62-
}
58+
if (!ASSERT_OK(bpf_qdisc_fifo__attach(fifo_skel), "bpf_qdisc_fifo__attach"))
59+
goto out;
6360

6461
do_test("bpf_fifo");
65-
66-
bpf_link__destroy(link);
62+
out:
6763
bpf_qdisc_fifo__destroy(fifo_skel);
6864
}
6965

7066
static void test_fq(void)
7167
{
7268
struct bpf_qdisc_fq *fq_skel;
73-
struct bpf_link *link;
7469

7570
fq_skel = bpf_qdisc_fq__open_and_load();
7671
if (!ASSERT_OK_PTR(fq_skel, "bpf_qdisc_fq__open_and_load"))
7772
return;
7873

79-
link = bpf_map__attach_struct_ops(fq_skel->maps.fq);
80-
if (!ASSERT_OK_PTR(link, "bpf_map__attach_struct_ops")) {
81-
bpf_qdisc_fq__destroy(fq_skel);
82-
return;
83-
}
74+
if (!ASSERT_OK(bpf_qdisc_fq__attach(fq_skel), "bpf_qdisc_fq__attach"))
75+
goto out;
8476

8577
do_test("bpf_fq");
86-
87-
bpf_link__destroy(link);
78+
out:
8879
bpf_qdisc_fq__destroy(fq_skel);
8980
}
9081

@@ -96,18 +87,14 @@ static void test_qdisc_attach_to_mq(void)
9687
.handle = 0x11 << 16,
9788
.qdisc = "bpf_fifo");
9889
struct bpf_qdisc_fifo *fifo_skel;
99-
struct bpf_link *link;
10090
int err;
10191

10292
fifo_skel = bpf_qdisc_fifo__open_and_load();
10393
if (!ASSERT_OK_PTR(fifo_skel, "bpf_qdisc_fifo__open_and_load"))
10494
return;
10595

106-
link = bpf_map__attach_struct_ops(fifo_skel->maps.fifo);
107-
if (!ASSERT_OK_PTR(link, "bpf_map__attach_struct_ops")) {
108-
bpf_qdisc_fifo__destroy(fifo_skel);
109-
return;
110-
}
96+
if (!ASSERT_OK(bpf_qdisc_fifo__attach(fifo_skel), "bpf_qdisc_fifo__attach"))
97+
goto out;
11198

11299
SYS(out, "ip link add veth0 type veth peer veth1");
113100
hook.ifindex = if_nametoindex("veth0");
@@ -120,7 +107,6 @@ static void test_qdisc_attach_to_mq(void)
120107

121108
SYS(out, "tc qdisc delete dev veth0 root mq");
122109
out:
123-
bpf_link__destroy(link);
124110
bpf_qdisc_fifo__destroy(fifo_skel);
125111
}
126112

@@ -132,18 +118,14 @@ static void test_qdisc_attach_to_non_root(void)
132118
.handle = 0x11 << 16,
133119
.qdisc = "bpf_fifo");
134120
struct bpf_qdisc_fifo *fifo_skel;
135-
struct bpf_link *link;
136121
int err;
137122

138123
fifo_skel = bpf_qdisc_fifo__open_and_load();
139124
if (!ASSERT_OK_PTR(fifo_skel, "bpf_qdisc_fifo__open_and_load"))
140125
return;
141126

142-
link = bpf_map__attach_struct_ops(fifo_skel->maps.fifo);
143-
if (!ASSERT_OK_PTR(link, "bpf_map__attach_struct_ops")) {
144-
bpf_qdisc_fifo__destroy(fifo_skel);
145-
return;
146-
}
127+
if (!ASSERT_OK(bpf_qdisc_fifo__attach(fifo_skel), "bpf_qdisc_fifo__attach"))
128+
goto out;
147129

148130
SYS(out, "tc qdisc add dev lo root handle 1: htb");
149131
SYS(out_del_htb, "tc class add dev lo parent 1: classid 1:1 htb rate 75Kbit");
@@ -155,18 +137,82 @@ static void test_qdisc_attach_to_non_root(void)
155137
out_del_htb:
156138
SYS(out, "tc qdisc delete dev lo root htb");
157139
out:
158-
bpf_link__destroy(link);
159140
bpf_qdisc_fifo__destroy(fifo_skel);
160141
}
161142

162-
void test_bpf_qdisc(void)
143+
static void test_incompl_ops(void)
144+
{
145+
struct bpf_qdisc_fail__incompl_ops *skel;
146+
struct bpf_link *link;
147+
148+
skel = bpf_qdisc_fail__incompl_ops__open_and_load();
149+
if (!ASSERT_OK_PTR(skel, "bpf_qdisc_fifo__open_and_load"))
150+
return;
151+
152+
link = bpf_map__attach_struct_ops(skel->maps.test);
153+
if (!ASSERT_ERR_PTR(link, "bpf_map__attach_struct_ops"))
154+
bpf_link__destroy(link);
155+
156+
bpf_qdisc_fail__incompl_ops__destroy(skel);
157+
}
158+
159+
static int get_default_qdisc(char *qdisc_name)
163160
{
164-
struct netns_obj *netns;
161+
FILE *f;
162+
int num;
163+
164+
f = fopen("/proc/sys/net/core/default_qdisc", "r");
165+
if (!f)
166+
return -errno;
167+
168+
num = fscanf(f, "%s", qdisc_name);
169+
fclose(f);
170+
171+
return num == 1 ? 0 : -EFAULT;
172+
}
173+
174+
static void test_default_qdisc_attach_to_mq(void)
175+
{
176+
char default_qdisc[IFNAMSIZ] = {};
177+
struct bpf_qdisc_fifo *fifo_skel;
178+
struct netns_obj *netns = NULL;
179+
int err;
180+
181+
fifo_skel = bpf_qdisc_fifo__open_and_load();
182+
if (!ASSERT_OK_PTR(fifo_skel, "bpf_qdisc_fifo__open_and_load"))
183+
return;
184+
185+
if (!ASSERT_OK(bpf_qdisc_fifo__attach(fifo_skel), "bpf_qdisc_fifo__attach"))
186+
goto out;
187+
188+
err = get_default_qdisc(default_qdisc);
189+
if (!ASSERT_OK(err, "read sysctl net.core.default_qdisc"))
190+
goto out;
191+
192+
err = write_sysctl("/proc/sys/net/core/default_qdisc", "bpf_fifo");
193+
if (!ASSERT_OK(err, "write sysctl net.core.default_qdisc"))
194+
goto out;
165195

166196
netns = netns_new("bpf_qdisc_ns", true);
167197
if (!ASSERT_OK_PTR(netns, "netns_new"))
168-
return;
198+
goto out;
199+
200+
SYS(out, "ip link add veth0 type veth peer veth1");
201+
SYS(out, "tc qdisc add dev veth0 root handle 1: mq");
169202

203+
ASSERT_EQ(fifo_skel->bss->init_called, true, "init_called");
204+
205+
SYS(out, "tc qdisc delete dev veth0 root mq");
206+
out:
207+
netns_free(netns);
208+
if (default_qdisc[0])
209+
write_sysctl("/proc/sys/net/core/default_qdisc", default_qdisc);
210+
211+
bpf_qdisc_fifo__destroy(fifo_skel);
212+
}
213+
214+
void test_ns_bpf_qdisc(void)
215+
{
170216
if (test__start_subtest("fifo"))
171217
test_fifo();
172218
if (test__start_subtest("fq"))
@@ -175,6 +221,11 @@ void test_bpf_qdisc(void)
175221
test_qdisc_attach_to_mq();
176222
if (test__start_subtest("attach to non root"))
177223
test_qdisc_attach_to_non_root();
224+
if (test__start_subtest("incompl_ops"))
225+
test_incompl_ops();
226+
}
178227

179-
netns_free(netns);
228+
void serial_test_bpf_qdisc_default(void)
229+
{
230+
test_default_qdisc_attach_to_mq();
180231
}

tools/testing/selftests/bpf/progs/bpf_qdisc_common.h

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,6 @@
1414

1515
struct bpf_sk_buff_ptr;
1616

17-
u32 bpf_skb_get_hash(struct sk_buff *p) __ksym;
18-
void bpf_kfree_skb(struct sk_buff *p) __ksym;
19-
void bpf_qdisc_skb_drop(struct sk_buff *p, struct bpf_sk_buff_ptr *to_free) __ksym;
20-
void bpf_qdisc_watchdog_schedule(struct Qdisc *sch, u64 expire, u64 delta_ns) __ksym;
21-
void bpf_qdisc_bstats_update(struct Qdisc *sch, const struct sk_buff *skb) __ksym;
22-
2317
static struct qdisc_skb_cb *qdisc_skb_cb(const struct sk_buff *skb)
2418
{
2519
return (struct qdisc_skb_cb *)skb->cb;
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
#include <vmlinux.h>
4+
#include "bpf_experimental.h"
5+
#include "bpf_qdisc_common.h"
6+
7+
char _license[] SEC("license") = "GPL";
8+
9+
SEC("struct_ops")
10+
int BPF_PROG(bpf_qdisc_test_enqueue, struct sk_buff *skb, struct Qdisc *sch,
11+
struct bpf_sk_buff_ptr *to_free)
12+
{
13+
bpf_qdisc_skb_drop(skb, to_free);
14+
return NET_XMIT_DROP;
15+
}
16+
17+
SEC("struct_ops")
18+
struct sk_buff *BPF_PROG(bpf_qdisc_test_dequeue, struct Qdisc *sch)
19+
{
20+
return NULL;
21+
}
22+
23+
SEC("struct_ops")
24+
void BPF_PROG(bpf_qdisc_test_reset, struct Qdisc *sch)
25+
{
26+
}
27+
28+
SEC("struct_ops")
29+
void BPF_PROG(bpf_qdisc_test_destroy, struct Qdisc *sch)
30+
{
31+
}
32+
33+
SEC(".struct_ops")
34+
struct Qdisc_ops test = {
35+
.enqueue = (void *)bpf_qdisc_test_enqueue,
36+
.dequeue = (void *)bpf_qdisc_test_dequeue,
37+
.reset = (void *)bpf_qdisc_test_reset,
38+
.destroy = (void *)bpf_qdisc_test_destroy,
39+
.id = "bpf_qdisc_test",
40+
};
41+

tools/testing/selftests/bpf/progs/bpf_qdisc_fifo.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ struct skb_node {
1414
private(A) struct bpf_spin_lock q_fifo_lock;
1515
private(A) struct bpf_list_head q_fifo __contains(skb_node, node);
1616

17+
bool init_called;
18+
1719
SEC("struct_ops/bpf_fifo_enqueue")
1820
int BPF_PROG(bpf_fifo_enqueue, struct sk_buff *skb, struct Qdisc *sch,
1921
struct bpf_sk_buff_ptr *to_free)
@@ -77,6 +79,7 @@ int BPF_PROG(bpf_fifo_init, struct Qdisc *sch, struct nlattr *opt,
7779
struct netlink_ext_ack *extack)
7880
{
7981
sch->limit = 1000;
82+
init_called = true;
8083
return 0;
8184
}
8285

@@ -106,12 +109,18 @@ void BPF_PROG(bpf_fifo_reset, struct Qdisc *sch)
106109
sch->q.qlen = 0;
107110
}
108111

112+
SEC("struct_ops")
113+
void BPF_PROG(bpf_fifo_destroy, struct Qdisc *sch)
114+
{
115+
}
116+
109117
SEC(".struct_ops")
110118
struct Qdisc_ops fifo = {
111119
.enqueue = (void *)bpf_fifo_enqueue,
112120
.dequeue = (void *)bpf_fifo_dequeue,
113121
.init = (void *)bpf_fifo_init,
114122
.reset = (void *)bpf_fifo_reset,
123+
.destroy = (void *)bpf_fifo_destroy,
115124
.id = "bpf_fifo",
116125
};
117126

tools/testing/selftests/bpf/progs/bpf_qdisc_fq.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -740,11 +740,17 @@ int BPF_PROG(bpf_fq_init, struct Qdisc *sch, struct nlattr *opt,
740740
return 0;
741741
}
742742

743+
SEC("struct_ops")
744+
void BPF_PROG(bpf_fq_destroy, struct Qdisc *sch)
745+
{
746+
}
747+
743748
SEC(".struct_ops")
744749
struct Qdisc_ops fq = {
745750
.enqueue = (void *)bpf_fq_enqueue,
746751
.dequeue = (void *)bpf_fq_dequeue,
747752
.reset = (void *)bpf_fq_reset,
748753
.init = (void *)bpf_fq_init,
754+
.destroy = (void *)bpf_fq_destroy,
749755
.id = "bpf_fq",
750756
};

0 commit comments

Comments
 (0)