Skip to content

bpf, xdp: clean adjust_{head,meta} memory when offset < 0 #8725

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
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
8 changes: 5 additions & 3 deletions include/uapi/linux/bpf.h
Original file line number Diff line number Diff line change
Expand Up @@ -2765,8 +2765,9 @@ union bpf_attr {
*
* long bpf_xdp_adjust_head(struct xdp_buff *xdp_md, int delta)
* Description
* Adjust (move) *xdp_md*\ **->data** by *delta* bytes. Note that
* it is possible to use a negative value for *delta*. This helper
* Adjust (move) *xdp_md*\ **->data** by *delta* bytes. Note that
* it is possible to use a negative value for *delta*. If *delta*
* is negative, the new header will be memset to zero. This helper
* can be used to prepare the packet for pushing or popping
* headers.
*
Expand Down Expand Up @@ -2994,7 +2995,8 @@ union bpf_attr {
* long bpf_xdp_adjust_meta(struct xdp_buff *xdp_md, int delta)
* Description
* Adjust the address pointed by *xdp_md*\ **->data_meta** by
* *delta* (which can be positive or negative). Note that this
* *delta* (which can be positive or negative). If *delta* is
* negative, the new meta will be memset to zero. Note that this
* operation modifies the address stored in *xdp_md*\ **->data**,
* so the latter must be loaded only after the helper has been
* called.
Expand Down
5 changes: 4 additions & 1 deletion net/core/filter.c
Original file line number Diff line number Diff line change
Expand Up @@ -3947,6 +3947,8 @@ BPF_CALL_2(bpf_xdp_adjust_head, struct xdp_buff *, xdp, int, offset)
if (metalen)
memmove(xdp->data_meta + offset,
xdp->data_meta, metalen);
if (offset < 0)
memset(data, 0, -offset);
xdp->data_meta += offset;
xdp->data = data;

Expand Down Expand Up @@ -4239,7 +4241,8 @@ BPF_CALL_2(bpf_xdp_adjust_meta, struct xdp_buff *, xdp, int, offset)
return -EINVAL;
if (unlikely(xdp_metalen_invalid(metalen)))
return -EACCES;

if (offset < 0)
memset(meta, 0, -offset);
xdp->data_meta = meta;

return 0;
Expand Down
6 changes: 4 additions & 2 deletions tools/include/uapi/linux/bpf.h
Original file line number Diff line number Diff line change
Expand Up @@ -2766,7 +2766,8 @@ union bpf_attr {
* long bpf_xdp_adjust_head(struct xdp_buff *xdp_md, int delta)
* Description
* Adjust (move) *xdp_md*\ **->data** by *delta* bytes. Note that
* it is possible to use a negative value for *delta*. This helper
* it is possible to use a negative value for *delta*. If *delta*
* is negative, the new header will be memset to zero. This helper
* can be used to prepare the packet for pushing or popping
* headers.
*
Expand Down Expand Up @@ -2994,7 +2995,8 @@ union bpf_attr {
* long bpf_xdp_adjust_meta(struct xdp_buff *xdp_md, int delta)
* Description
* Adjust the address pointed by *xdp_md*\ **->data_meta** by
* *delta* (which can be positive or negative). Note that this
* *delta* (which can be positive or negative). If *delta* is
* negative, the new meta will be memset to zero. Note that this
* operation modifies the address stored in *xdp_md*\ **->data**,
* so the latter must be loaded only after the helper has been
* called.
Expand Down
52 changes: 45 additions & 7 deletions tools/testing/selftests/bpf/prog_tests/xdp_perf.c
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
// SPDX-License-Identifier: GPL-2.0
#include <test_progs.h>
#include <network_helpers.h>
#include "xdp_dummy.skel.h"

void test_xdp_perf(void)
{
const char *file = "./xdp_dummy.bpf.o";
struct bpf_object *obj;
struct xdp_dummy *skel;
char in[128], out[128];
int err, prog_fd;
LIBBPF_OPTS(bpf_test_run_opts, topts,
Expand All @@ -15,14 +16,51 @@ void test_xdp_perf(void)
.repeat = 1000000,
);

err = bpf_prog_test_load(file, BPF_PROG_TYPE_XDP, &obj, &prog_fd);
if (CHECK_FAIL(err))
return;

skel = xdp_dummy__open_and_load();
prog_fd = bpf_program__fd(skel->progs.xdp_dummy_prog);
err = bpf_prog_test_run_opts(prog_fd, &topts);
ASSERT_OK(err, "test_run");
ASSERT_EQ(topts.retval, XDP_PASS, "test_run retval");
ASSERT_EQ(topts.data_size_out, 128, "test_run data_size_out");

bpf_object__close(obj);
xdp_dummy__destroy(skel);
}

void test_xdp_adjust_head_perf(void)
{
struct xdp_dummy *skel;
int repeat = 9000000;
struct xdp_md ctx_in;
char data[100];
int err, prog_fd;
size_t test_header_size[] = {
ETH_ALEN,
sizeof(struct iphdr),
sizeof(struct ipv6hdr),
200,
};
DECLARE_LIBBPF_OPTS(bpf_test_run_opts, topts,
.data_in = &data,
.data_size_in = sizeof(data),
.repeat = repeat,
);

topts.ctx_in = &ctx_in;
topts.ctx_size_in = sizeof(ctx_in);
memset(&ctx_in, 0, sizeof(ctx_in));
ctx_in.data_meta = 0;
ctx_in.data_end = ctx_in.data + sizeof(data);

skel = xdp_dummy__open_and_load();
prog_fd = bpf_program__fd(skel->progs.xdp_dummy_adjust_head);

for (int i = 0; i < ARRAY_SIZE(test_header_size); i++) {
skel->bss->head_size = test_header_size[i];
err = bpf_prog_test_run_opts(prog_fd, &topts);
ASSERT_OK(err, "test_run");
ASSERT_EQ(topts.retval, XDP_PASS, "test_run retval");
fprintf(stdout, "run adjust head with size %zd cost %d ns\n",
test_header_size[i], topts.duration);
}
xdp_dummy__destroy(skel);
}
14 changes: 14 additions & 0 deletions tools/testing/selftests/bpf/progs/xdp_dummy.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,24 @@
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>

int head_size;

SEC("xdp")
int xdp_dummy_prog(struct xdp_md *ctx)
{
return XDP_PASS;
}

SEC("xdp")
int xdp_dummy_adjust_head(struct xdp_md *ctx)
{
if (bpf_xdp_adjust_head(ctx, -head_size))
return XDP_DROP;

if (bpf_xdp_adjust_head(ctx, head_size))
return XDP_DROP;

return XDP_PASS;
}

char _license[] SEC("license") = "GPL";
Loading