Skip to content

Commit 99004b6

Browse files
pchaignoKernel Patches Daemon
authored andcommitted
bpf: Craft non-linear skbs in BPF_PROG_TEST_RUN
This patch adds support for crafting non-linear skbs in BPF test runs for tc programs, via a new flag BPF_F_TEST_SKB_NON_LINEAR. When this flag is set, only the L2 header is pulled in the linear area. This is particularly useful to test support for non-linear skbs in large codebases such as Cilium. We've had multiple bugs in the past few years where we were missing calls to bpf_skb_pull_data(). This support in BPF_PROG_TEST_RUN would allow us to automatically cover this case in our BPF tests. In addition to the selftests introduced later in the series, this patch was tested by setting BPF_F_TEST_SKB_NON_LINEAR for all tc selftests programs and checking test failures were expected. Suggested-by: Daniel Borkmann <[email protected]> Signed-off-by: Paul Chaignon <[email protected]>
1 parent 72c935c commit 99004b6

File tree

3 files changed

+66
-26
lines changed

3 files changed

+66
-26
lines changed

include/uapi/linux/bpf.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1448,6 +1448,8 @@ enum {
14481448
#define BPF_F_TEST_XDP_LIVE_FRAMES (1U << 1)
14491449
/* If set, apply CHECKSUM_COMPLETE to skb and validate the checksum */
14501450
#define BPF_F_TEST_SKB_CHECKSUM_COMPLETE (1U << 2)
1451+
/* If set, skb will be non-linear */
1452+
#define BPF_F_TEST_SKB_NON_LINEAR (1U << 3)
14511453

14521454
/* type for BPF_ENABLE_STATS */
14531455
enum bpf_stats_type {

net/bpf/test_run.c

Lines changed: 62 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -660,20 +660,29 @@ BTF_ID_FLAGS(func, bpf_kfunc_call_memb_release, KF_RELEASE)
660660
BTF_KFUNCS_END(test_sk_check_kfunc_ids)
661661

662662
static void *bpf_test_init(const union bpf_attr *kattr, u32 user_size,
663-
u32 size, u32 headroom, u32 tailroom)
663+
u32 size, u32 headroom, u32 tailroom, bool nonlinear)
664664
{
665665
void __user *data_in = u64_to_user_ptr(kattr->test.data_in);
666-
void *data;
666+
void *data, *dst;
667667

668668
if (user_size < ETH_HLEN || user_size > PAGE_SIZE - headroom - tailroom)
669669
return ERR_PTR(-EINVAL);
670670

671-
size = SKB_DATA_ALIGN(size);
672-
data = kzalloc(size + headroom + tailroom, GFP_USER);
671+
/* In non-linear case, data_in is copied to the paged data */
672+
if (nonlinear) {
673+
data = alloc_page(GFP_USER);
674+
} else {
675+
size = SKB_DATA_ALIGN(size);
676+
data = kzalloc(size + headroom + tailroom, GFP_USER);
677+
}
673678
if (!data)
674679
return ERR_PTR(-ENOMEM);
675680

676-
if (copy_from_user(data + headroom, data_in, user_size)) {
681+
if (nonlinear)
682+
dst = page_address(data);
683+
else
684+
dst = data + headroom;
685+
if (copy_from_user(dst, data_in, user_size)) {
677686
kfree(data);
678687
return ERR_PTR(-EFAULT);
679688
}
@@ -984,7 +993,7 @@ static struct proto bpf_dummy_proto = {
984993
int bpf_prog_test_run_skb(struct bpf_prog *prog, const union bpf_attr *kattr,
985994
union bpf_attr __user *uattr)
986995
{
987-
bool is_l2 = false, is_direct_pkt_access = false;
996+
bool is_l2 = false, is_direct_pkt_access = false, is_nonlinear = false;
988997
struct net *net = current->nsproxy->net_ns;
989998
struct net_device *dev = net->loopback_dev;
990999
u32 size = kattr->test.data_size_in;
@@ -997,21 +1006,11 @@ int bpf_prog_test_run_skb(struct bpf_prog *prog, const union bpf_attr *kattr,
9971006
void *data;
9981007
int ret;
9991008

1000-
if ((kattr->test.flags & ~BPF_F_TEST_SKB_CHECKSUM_COMPLETE) ||
1009+
if ((kattr->test.flags & ~(BPF_F_TEST_SKB_CHECKSUM_COMPLETE | BPF_F_TEST_SKB_NON_LINEAR)) ||
10011010
kattr->test.cpu || kattr->test.batch_size)
10021011
return -EINVAL;
10031012

1004-
data = bpf_test_init(kattr, kattr->test.data_size_in,
1005-
size, NET_SKB_PAD + NET_IP_ALIGN,
1006-
SKB_DATA_ALIGN(sizeof(struct skb_shared_info)));
1007-
if (IS_ERR(data))
1008-
return PTR_ERR(data);
1009-
1010-
ctx = bpf_ctx_init(kattr, sizeof(struct __sk_buff));
1011-
if (IS_ERR(ctx)) {
1012-
ret = PTR_ERR(ctx);
1013-
goto out;
1014-
}
1013+
is_nonlinear = kattr->test.flags & BPF_F_TEST_SKB_NON_LINEAR;
10151014

10161015
switch (prog->type) {
10171016
case BPF_PROG_TYPE_SCHED_CLS:
@@ -1028,22 +1027,55 @@ int bpf_prog_test_run_skb(struct bpf_prog *prog, const union bpf_attr *kattr,
10281027
break;
10291028
}
10301029

1030+
if (is_nonlinear && !is_l2)
1031+
return -EINVAL;
1032+
1033+
data = bpf_test_init(kattr, kattr->test.data_size_in,
1034+
size, NET_SKB_PAD + NET_IP_ALIGN,
1035+
SKB_DATA_ALIGN(sizeof(struct skb_shared_info)),
1036+
is_nonlinear);
1037+
if (IS_ERR(data))
1038+
return PTR_ERR(data);
1039+
1040+
ctx = bpf_ctx_init(kattr, sizeof(struct __sk_buff));
1041+
if (IS_ERR(ctx)) {
1042+
ret = PTR_ERR(ctx);
1043+
goto out;
1044+
}
1045+
10311046
sk = sk_alloc(net, AF_UNSPEC, GFP_USER, &bpf_dummy_proto, 1);
10321047
if (!sk) {
10331048
ret = -ENOMEM;
10341049
goto out;
10351050
}
10361051
sock_init_data(NULL, sk);
10371052

1038-
skb = slab_build_skb(data);
1053+
if (is_nonlinear)
1054+
skb = alloc_skb(NET_SKB_PAD + NET_IP_ALIGN + size +
1055+
SKB_DATA_ALIGN(sizeof(struct skb_shared_info)),
1056+
GFP_USER);
1057+
else
1058+
skb = slab_build_skb(data);
10391059
if (!skb) {
10401060
ret = -ENOMEM;
10411061
goto out;
10421062
}
1063+
10431064
skb->sk = sk;
10441065

10451066
skb_reserve(skb, NET_SKB_PAD + NET_IP_ALIGN);
1046-
__skb_put(skb, size);
1067+
1068+
if (is_nonlinear) {
1069+
skb_fill_page_desc(skb, 0, data, 0, size);
1070+
skb->truesize += PAGE_SIZE;
1071+
skb->data_len = size;
1072+
skb->len = size;
1073+
1074+
/* eth_type_trans expects the Ethernet header in the linear area. */
1075+
__pskb_pull_tail(skb, ETH_HLEN);
1076+
} else {
1077+
__skb_put(skb, size);
1078+
}
10471079

10481080
data = NULL; /* data released via kfree_skb */
10491081

@@ -1126,9 +1158,11 @@ int bpf_prog_test_run_skb(struct bpf_prog *prog, const union bpf_attr *kattr,
11261158
convert_skb_to___skb(skb, ctx);
11271159

11281160
size = skb->len;
1129-
/* bpf program can never convert linear skb to non-linear */
1130-
if (WARN_ON_ONCE(skb_is_nonlinear(skb)))
1161+
if (skb_is_nonlinear(skb)) {
1162+
/* bpf program can never convert linear skb to non-linear */
1163+
WARN_ON_ONCE(!is_nonlinear);
11311164
size = skb_headlen(skb);
1165+
}
11321166
ret = bpf_test_finish(kattr, uattr, skb->data, NULL, size, retval,
11331167
duration);
11341168
if (!ret)
@@ -1138,7 +1172,8 @@ int bpf_prog_test_run_skb(struct bpf_prog *prog, const union bpf_attr *kattr,
11381172
if (dev && dev != net->loopback_dev)
11391173
dev_put(dev);
11401174
kfree_skb(skb);
1141-
kfree(data);
1175+
if (data)
1176+
is_nonlinear ? __free_page(data) : kfree(data);
11421177
if (sk)
11431178
sk_free(sk);
11441179
kfree(ctx);
@@ -1264,7 +1299,7 @@ int bpf_prog_test_run_xdp(struct bpf_prog *prog, const union bpf_attr *kattr,
12641299
size = max_data_sz;
12651300
}
12661301

1267-
data = bpf_test_init(kattr, size, max_data_sz, headroom, tailroom);
1302+
data = bpf_test_init(kattr, size, max_data_sz, headroom, tailroom, false);
12681303
if (IS_ERR(data)) {
12691304
ret = PTR_ERR(data);
12701305
goto free_ctx;
@@ -1387,7 +1422,7 @@ int bpf_prog_test_run_flow_dissector(struct bpf_prog *prog,
13871422
if (size < ETH_HLEN)
13881423
return -EINVAL;
13891424

1390-
data = bpf_test_init(kattr, kattr->test.data_size_in, size, 0, 0);
1425+
data = bpf_test_init(kattr, kattr->test.data_size_in, size, 0, 0, false);
13911426
if (IS_ERR(data))
13921427
return PTR_ERR(data);
13931428

@@ -1660,7 +1695,8 @@ int bpf_prog_test_run_nf(struct bpf_prog *prog,
16601695

16611696
data = bpf_test_init(kattr, kattr->test.data_size_in, size,
16621697
NET_SKB_PAD + NET_IP_ALIGN,
1663-
SKB_DATA_ALIGN(sizeof(struct skb_shared_info)));
1698+
SKB_DATA_ALIGN(sizeof(struct skb_shared_info)),
1699+
false);
16641700
if (IS_ERR(data))
16651701
return PTR_ERR(data);
16661702

tools/include/uapi/linux/bpf.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1448,6 +1448,8 @@ enum {
14481448
#define BPF_F_TEST_XDP_LIVE_FRAMES (1U << 1)
14491449
/* If set, apply CHECKSUM_COMPLETE to skb and validate the checksum */
14501450
#define BPF_F_TEST_SKB_CHECKSUM_COMPLETE (1U << 2)
1451+
/* If set, skb will be non-linear */
1452+
#define BPF_F_TEST_SKB_NON_LINEAR (1U << 3)
14511453

14521454
/* type for BPF_ENABLE_STATS */
14531455
enum bpf_stats_type {

0 commit comments

Comments
 (0)