Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 82 additions & 25 deletions net/bpf/test_run.c
Original file line number Diff line number Diff line change
Expand Up @@ -910,6 +910,12 @@ static int convert___skb_to_skb(struct sk_buff *skb, struct __sk_buff *__skb)
/* cb is allowed */

if (!range_is_zero(__skb, offsetofend(struct __sk_buff, cb),
offsetof(struct __sk_buff, data_end)))
return -EINVAL;

/* data_end is allowed, but not copied to skb */

if (!range_is_zero(__skb, offsetofend(struct __sk_buff, data_end),
offsetof(struct __sk_buff, tstamp)))
return -EINVAL;

Expand Down Expand Up @@ -984,16 +990,19 @@ static struct proto bpf_dummy_proto = {
int bpf_prog_test_run_skb(struct bpf_prog *prog, const union bpf_attr *kattr,
union bpf_attr __user *uattr)
{
u32 tailroom = SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
bool is_l2 = false, is_direct_pkt_access = false;
struct net *net = current->nsproxy->net_ns;
struct net_device *dev = net->loopback_dev;
u32 headroom = NET_SKB_PAD + NET_IP_ALIGN;
u32 linear_sz = kattr->test.data_size_in;
u32 size = kattr->test.data_size_in;
u32 repeat = kattr->test.repeat;
struct __sk_buff *ctx = NULL;
struct sk_buff *skb = NULL;
struct sock *sk = NULL;
u32 retval, duration;
int hh_len = ETH_HLEN;
struct sk_buff *skb;
struct sock *sk;
void *data;
int ret;

Expand All @@ -1004,18 +1013,6 @@ int bpf_prog_test_run_skb(struct bpf_prog *prog, const union bpf_attr *kattr,
if (size < ETH_HLEN)
return -EINVAL;

data = bpf_test_init(kattr, kattr->test.data_size_in,
size, NET_SKB_PAD + NET_IP_ALIGN,
SKB_DATA_ALIGN(sizeof(struct skb_shared_info)));
if (IS_ERR(data))
return PTR_ERR(data);

ctx = bpf_ctx_init(kattr, sizeof(struct __sk_buff));
if (IS_ERR(ctx)) {
kfree(data);
return PTR_ERR(ctx);
}

switch (prog->type) {
case BPF_PROG_TYPE_SCHED_CLS:
case BPF_PROG_TYPE_SCHED_ACT:
Expand All @@ -1031,25 +1028,81 @@ int bpf_prog_test_run_skb(struct bpf_prog *prog, const union bpf_attr *kattr,
break;
}

ctx = bpf_ctx_init(kattr, sizeof(struct __sk_buff));
if (IS_ERR(ctx))
return PTR_ERR(ctx);

if (ctx) {
if (!is_l2 || ctx->data_end > kattr->test.data_size_in) {
ret = -EINVAL;
goto out;
}
if (ctx->data_end)
linear_sz = max(ETH_HLEN, ctx->data_end);
}

data = bpf_test_init(kattr, linear_sz, size, headroom, tailroom);
if (IS_ERR(data)) {
ret = PTR_ERR(data);
data = NULL;
goto out;
}

sk = sk_alloc(net, AF_UNSPEC, GFP_USER, &bpf_dummy_proto, 1);
if (!sk) {
kfree(data);
kfree(ctx);
return -ENOMEM;
ret = -ENOMEM;
goto out;
}
sock_init_data(NULL, sk);

skb = slab_build_skb(data);
if (!skb) {
kfree(data);
kfree(ctx);
sk_free(sk);
return -ENOMEM;
ret = -ENOMEM;
goto out;
}

skb->sk = sk;

skb_reserve(skb, NET_SKB_PAD + NET_IP_ALIGN);
__skb_put(skb, size);
__skb_put(skb, linear_sz);

if (unlikely(kattr->test.data_size_in > linear_sz)) {
void __user *data_in = u64_to_user_ptr(kattr->test.data_in);
struct skb_shared_info *sinfo = skb_shinfo(skb);

size = linear_sz;
while (size < kattr->test.data_size_in) {
struct page *page;
u32 data_len;

if (sinfo->nr_frags == MAX_SKB_FRAGS) {
ret = -ENOMEM;
goto out;
}

page = alloc_page(GFP_KERNEL);
if (!page) {
ret = -ENOMEM;
goto out;
}

data_len = min_t(u32, kattr->test.data_size_in - size,
PAGE_SIZE);
skb_fill_page_desc(skb, sinfo->nr_frags, page, 0, data_len);

if (copy_from_user(page_address(page), data_in + size,
data_len)) {
ret = -EFAULT;
goto out;
}
skb->data_len += data_len;
skb->truesize += PAGE_SIZE;
skb->len += data_len;
size += data_len;
}
}

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

if (ctx && ctx->ifindex > 1) {
dev = dev_get_by_index(net, ctx->ifindex);
Expand Down Expand Up @@ -1130,9 +1183,11 @@ int bpf_prog_test_run_skb(struct bpf_prog *prog, const union bpf_attr *kattr,
convert_skb_to___skb(skb, ctx);

size = skb->len;
/* bpf program can never convert linear skb to non-linear */
if (WARN_ON_ONCE(skb_is_nonlinear(skb)))
if (skb_is_nonlinear(skb)) {
/* bpf program can never convert linear skb to non-linear */
WARN_ON_ONCE(linear_sz == size);
size = skb_headlen(skb);
}
ret = bpf_test_finish(kattr, uattr, skb->data, NULL, size, retval,
duration);
if (!ret)
Expand All @@ -1142,7 +1197,9 @@ int bpf_prog_test_run_skb(struct bpf_prog *prog, const union bpf_attr *kattr,
if (dev && dev != net->loopback_dev)
dev_put(dev);
kfree_skb(skb);
sk_free(sk);
kfree(data);
if (sk)
sk_free(sk);
kfree(ctx);
return ret;
}
Expand Down
4 changes: 4 additions & 0 deletions tools/testing/selftests/bpf/progs/bpf_misc.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,9 @@
* Several __arch_* annotations could be specified at once.
* When test case is not run on current arch it is marked as skipped.
* __caps_unpriv Specify the capabilities that should be set when running the test.
*
* __linear_size Specify the size of the linear area of non-linear skbs, or
* 0 for linear skbs.
*/
#define __msg(msg) __attribute__((btf_decl_tag("comment:test_expect_msg=" XSTR(__COUNTER__) "=" msg)))
#define __not_msg(msg) __attribute__((btf_decl_tag("comment:test_expect_not_msg=" XSTR(__COUNTER__) "=" msg)))
Expand Down Expand Up @@ -159,6 +162,7 @@
#define __stderr_unpriv(msg) __attribute__((btf_decl_tag("comment:test_expect_stderr_unpriv=" XSTR(__COUNTER__) "=" msg)))
#define __stdout(msg) __attribute__((btf_decl_tag("comment:test_expect_stdout=" XSTR(__COUNTER__) "=" msg)))
#define __stdout_unpriv(msg) __attribute__((btf_decl_tag("comment:test_expect_stdout_unpriv=" XSTR(__COUNTER__) "=" msg)))
#define __linear_size(sz) __attribute__((btf_decl_tag("comment:test_linear_size=" XSTR(sz))))

/* Define common capabilities tested using __caps_unpriv */
#define CAP_NET_ADMIN 12
Expand Down
54 changes: 54 additions & 0 deletions tools/testing/selftests/bpf/progs/verifier_direct_packet_access.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
/* Converted from tools/testing/selftests/bpf/verifier/direct_packet_access.c */

#include <linux/if_ether.h>
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
#include "bpf_misc.h"
Expand Down Expand Up @@ -800,4 +801,57 @@ l0_%=: /* exit(0) */ \
: __clobber_all);
}

#define access_test_non_linear(name, desc, retval, linear_sz) \
SEC("tc") \
__description("direct packet access: " #name " (non-linear, " desc ")") \
__success __retval(retval) \
__linear_size(linear_sz) \
__naked void access_##name(void) \
{ \
asm volatile (" \
r2 = *(u32*)(r1 + %[skb_data]); \
r3 = *(u32*)(r1 + %[skb_data_end]); \
r0 = r2; \
r0 += 22; \
if r0 > r3 goto l0_%=; \
r0 = *(u8*)(r0 - 1); \
exit; \
l0_%=: r0 = 1; \
exit; \
" : \
: __imm_const(skb_data, offsetof(struct __sk_buff, data)), \
__imm_const(skb_data_end, offsetof(struct __sk_buff, data_end)) \
: __clobber_all); \
}

access_test_non_linear(test31, "too short eth", 1, ETH_HLEN);
access_test_non_linear(test32, "too short 1", 1, 1);
access_test_non_linear(test33, "long enough", 0, 22);

SEC("tc")
__description("direct packet access: test34 (non-linear, linearized)")
__success __retval(0)
__linear_size(ETH_HLEN)
__naked void access_test34_non_linear_linearized(void)
{
asm volatile (" \
r6 = r1; \
r2 = 22; \
call %[bpf_skb_pull_data]; \
r2 = *(u32*)(r6 + %[skb_data]); \
r3 = *(u32*)(r6 + %[skb_data_end]); \
r0 = r2; \
r0 += 22; \
if r0 > r3 goto l0_%=; \
r0 = *(u8*)(r0 - 1); \
exit; \
l0_%=: r0 = 1; \
exit; \
" :
: __imm(bpf_skb_pull_data),
__imm_const(skb_data, offsetof(struct __sk_buff, data)),
__imm_const(skb_data_end, offsetof(struct __sk_buff, data_end))
: __clobber_all);
}

char _license[] SEC("license") = "GPL";
19 changes: 17 additions & 2 deletions tools/testing/selftests/bpf/test_loader.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
#define TEST_TAG_EXPECT_STDERR_PFX_UNPRIV "comment:test_expect_stderr_unpriv="
#define TEST_TAG_EXPECT_STDOUT_PFX "comment:test_expect_stdout="
#define TEST_TAG_EXPECT_STDOUT_PFX_UNPRIV "comment:test_expect_stdout_unpriv="
#define TEST_TAG_LINEAR_SIZE "comment:test_linear_size="

/* Warning: duplicated in bpf_misc.h */
#define POINTER_VALUE 0xbadcafe
Expand Down Expand Up @@ -89,6 +90,7 @@ struct test_spec {
int mode_mask;
int arch_mask;
int load_mask;
int linear_sz;
bool auxiliary;
bool valid;
};
Expand Down Expand Up @@ -633,6 +635,11 @@ static int parse_test_spec(struct test_loader *tester,
&spec->unpriv.stdout);
if (err)
goto cleanup;
} else if (str_has_pfx(s, TEST_TAG_LINEAR_SIZE)) {
val = s + sizeof(TEST_TAG_LINEAR_SIZE) - 1;
err = parse_int(val, &spec->linear_sz, "test linear size");
if (err)
goto cleanup;
}
}

Expand Down Expand Up @@ -1007,10 +1014,11 @@ static bool is_unpriv_capable_map(struct bpf_map *map)
}
}

static int do_prog_test_run(int fd_prog, int *retval, bool empty_opts)
static int do_prog_test_run(int fd_prog, int *retval, bool empty_opts, int linear_sz)
{
__u8 tmp_out[TEST_DATA_LEN << 2] = {};
__u8 tmp_in[TEST_DATA_LEN] = {};
struct __sk_buff ctx = {};
int err, saved_errno;
LIBBPF_OPTS(bpf_test_run_opts, topts,
.data_in = tmp_in,
Expand All @@ -1020,6 +1028,12 @@ static int do_prog_test_run(int fd_prog, int *retval, bool empty_opts)
.repeat = 1,
);

if (linear_sz) {
ctx.data_end = linear_sz;
topts.ctx_in = &ctx;
topts.ctx_size_in = sizeof(ctx);
}

if (empty_opts) {
memset(&topts, 0, sizeof(struct bpf_test_run_opts));
topts.sz = sizeof(struct bpf_test_run_opts);
Expand Down Expand Up @@ -1269,7 +1283,8 @@ void run_subtest(struct test_loader *tester,
}

err = do_prog_test_run(bpf_program__fd(tprog), &retval,
bpf_program__type(tprog) == BPF_PROG_TYPE_SYSCALL ? true : false);
bpf_program__type(tprog) == BPF_PROG_TYPE_SYSCALL ? true : false,
spec->linear_sz);
if (!err && retval != subspec->retval && subspec->retval != POINTER_VALUE) {
PRINT_FAIL("Unexpected retval: %d != %d\n", retval, subspec->retval);
goto tobj_cleanup;
Expand Down
Loading