Skip to content

Commit 52bdd47

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. The size of the linear area is given by ctx->data_end, with a minimum of ETH_HLEN always pulled in the linear area. If ctx or ctx->data_end are null, a linear skb is used. 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 enabling non-linear skbs for all tc selftests programs and checking test failures were expected. Tested-by: [email protected] Suggested-by: Daniel Borkmann <[email protected]> Signed-off-by: Paul Chaignon <[email protected]>
1 parent 1609bfe commit 52bdd47

File tree

1 file changed

+61
-6
lines changed

1 file changed

+61
-6
lines changed

net/bpf/test_run.c

Lines changed: 61 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -910,6 +910,12 @@ static int convert___skb_to_skb(struct sk_buff *skb, struct __sk_buff *__skb)
910910
/* cb is allowed */
911911

912912
if (!range_is_zero(__skb, offsetofend(struct __sk_buff, cb),
913+
offsetof(struct __sk_buff, data_end)))
914+
return -EINVAL;
915+
916+
/* data_end is allowed, but not copied to skb */
917+
918+
if (!range_is_zero(__skb, offsetofend(struct __sk_buff, data_end),
913919
offsetof(struct __sk_buff, tstamp)))
914920
return -EINVAL;
915921

@@ -984,9 +990,12 @@ static struct proto bpf_dummy_proto = {
984990
int bpf_prog_test_run_skb(struct bpf_prog *prog, const union bpf_attr *kattr,
985991
union bpf_attr __user *uattr)
986992
{
993+
u32 tailroom = SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
987994
bool is_l2 = false, is_direct_pkt_access = false;
988995
struct net *net = current->nsproxy->net_ns;
989996
struct net_device *dev = net->loopback_dev;
997+
u32 headroom = NET_SKB_PAD + NET_IP_ALIGN;
998+
u32 linear_sz = kattr->test.data_size_in;
990999
u32 size = kattr->test.data_size_in;
9911000
u32 repeat = kattr->test.repeat;
9921001
struct __sk_buff *ctx = NULL;
@@ -1023,9 +1032,16 @@ int bpf_prog_test_run_skb(struct bpf_prog *prog, const union bpf_attr *kattr,
10231032
if (IS_ERR(ctx))
10241033
return PTR_ERR(ctx);
10251034

1026-
data = bpf_test_init(kattr, kattr->test.data_size_in,
1027-
size, NET_SKB_PAD + NET_IP_ALIGN,
1028-
SKB_DATA_ALIGN(sizeof(struct skb_shared_info)));
1035+
if (ctx) {
1036+
if (!is_l2 || ctx->data_end > kattr->test.data_size_in) {
1037+
ret = -EINVAL;
1038+
goto out;
1039+
}
1040+
if (ctx->data_end)
1041+
linear_sz = max(ETH_HLEN, ctx->data_end);
1042+
}
1043+
1044+
data = bpf_test_init(kattr, linear_sz, size, headroom, tailroom);
10291045
if (IS_ERR(data)) {
10301046
ret = PTR_ERR(data);
10311047
data = NULL;
@@ -1044,10 +1060,47 @@ int bpf_prog_test_run_skb(struct bpf_prog *prog, const union bpf_attr *kattr,
10441060
ret = -ENOMEM;
10451061
goto out;
10461062
}
1063+
10471064
skb->sk = sk;
10481065

10491066
skb_reserve(skb, NET_SKB_PAD + NET_IP_ALIGN);
1050-
__skb_put(skb, size);
1067+
__skb_put(skb, linear_sz);
1068+
1069+
if (unlikely(kattr->test.data_size_in > linear_sz)) {
1070+
void __user *data_in = u64_to_user_ptr(kattr->test.data_in);
1071+
struct skb_shared_info *sinfo = skb_shinfo(skb);
1072+
1073+
size = linear_sz;
1074+
while (size < kattr->test.data_size_in) {
1075+
struct page *page;
1076+
u32 data_len;
1077+
1078+
if (sinfo->nr_frags == MAX_SKB_FRAGS) {
1079+
ret = -ENOMEM;
1080+
goto out;
1081+
}
1082+
1083+
page = alloc_page(GFP_KERNEL);
1084+
if (!page) {
1085+
ret = -ENOMEM;
1086+
goto out;
1087+
}
1088+
1089+
data_len = min_t(u32, kattr->test.data_size_in - size,
1090+
PAGE_SIZE);
1091+
skb_fill_page_desc(skb, sinfo->nr_frags, page, 0, data_len);
1092+
1093+
if (copy_from_user(page_address(page), data_in + size,
1094+
data_len)) {
1095+
ret = -EFAULT;
1096+
goto out;
1097+
}
1098+
skb->data_len += data_len;
1099+
skb->truesize += PAGE_SIZE;
1100+
skb->len += data_len;
1101+
size += data_len;
1102+
}
1103+
}
10511104

10521105
data = NULL; /* data released via kfree_skb */
10531106

@@ -1130,9 +1183,11 @@ int bpf_prog_test_run_skb(struct bpf_prog *prog, const union bpf_attr *kattr,
11301183
convert_skb_to___skb(skb, ctx);
11311184

11321185
size = skb->len;
1133-
/* bpf program can never convert linear skb to non-linear */
1134-
if (WARN_ON_ONCE(skb_is_nonlinear(skb)))
1186+
if (skb_is_nonlinear(skb)) {
1187+
/* bpf program can never convert linear skb to non-linear */
1188+
WARN_ON_ONCE(linear_sz == size);
11351189
size = skb_headlen(skb);
1190+
}
11361191
ret = bpf_test_finish(kattr, uattr, skb->data, NULL, size, retval,
11371192
duration);
11381193
if (!ret)

0 commit comments

Comments
 (0)