From: Matt Mullins <mmullins@fb.com>
To: "netdev@vger.kernel.org" <netdev@vger.kernel.org>,
Yonghong Song <yhs@fb.com>, Andrew Hall <hall@fb.com>,
"bpf@vger.kernel.org" <bpf@vger.kernel.org>,
"ast@kernel.org" <ast@kernel.org>
Cc: Song Liu <songliubraving@fb.com>,
"linux-kernel@vger.kernel.org" <linux-kernel@vger.kernel.org>,
"daniel@iogearbox.net" <daniel@iogearbox.net>,
"rostedt@goodmis.org" <rostedt@goodmis.org>,
"mingo@redhat.com" <mingo@redhat.com>,
"shuah@kernel.org" <shuah@kernel.org>, Martin Lau <kafai@fb.com>,
"linux-kselftest@vger.kernel.org"
<linux-kselftest@vger.kernel.org>,
"davem@davemloft.net" <davem@davemloft.net>
Subject: Re: [PATCH bpf-next v3 5/5] selftests: bpf: test writable buffers in raw tps
Date: Mon, 22 Apr 2019 19:27:02 +0000 [thread overview]
Message-ID: <7a40c2851cd6f708a11cd003a05ab79c8644b10d.camel@fb.com> (raw)
In-Reply-To: <7f32dd3c-a1e1-2965-3a98-3fa9e54e8501@fb.com>
On Mon, 2019-04-22 at 18:32 +0000, Yonghong Song wrote:
>
> On 4/19/19 2:04 PM, Matt Mullins wrote:
> > This tests that:
> > * a BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE cannot be attached if it
> > uses either:
> > * a variable offset to the tracepoint buffer, or
> > * an offset beyond the size of the tracepoint buffer
> > * a tracer can modify the buffer provided when attached to a writable
> > tracepoint in bpf_prog_test_run
> >
> > Signed-off-by: Matt Mullins <mmullins@fb.com>
> > ---
> > include/trace/events/bpf_test_run.h | 50 ++++++++++++
> > net/bpf/test_run.c | 4 +
> > .../raw_tp_writable_reject_nbd_invalid.c | 40 ++++++++++
> > .../bpf/prog_tests/raw_tp_writable_test_run.c | 80 +++++++++++++++++++
> > .../selftests/bpf/verifier/raw_tp_writable.c | 34 ++++++++
> > 5 files changed, 208 insertions(+)
> > create mode 100644 include/trace/events/bpf_test_run.h
> > create mode 100644 tools/testing/selftests/bpf/prog_tests/raw_tp_writable_reject_nbd_invalid.c
> > create mode 100644 tools/testing/selftests/bpf/prog_tests/raw_tp_writable_test_run.c
> > create mode 100644 tools/testing/selftests/bpf/verifier/raw_tp_writable.c
> >
> > diff --git a/include/trace/events/bpf_test_run.h b/include/trace/events/bpf_test_run.h
> > new file mode 100644
> > index 000000000000..abf466839ea4
> > --- /dev/null
> > +++ b/include/trace/events/bpf_test_run.h
> > @@ -0,0 +1,50 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +#undef TRACE_SYSTEM
> > +#define TRACE_SYSTEM bpf_test_run
> > +
> > +#if !defined(_TRACE_NBD_H) || defined(TRACE_HEADER_MULTI_READ)
> > +#define _TRACE_BPF_TEST_RUN_H
> > +
> > +#include <linux/tracepoint.h>
> > +
> > +DECLARE_EVENT_CLASS(bpf_test_finish,
> > +
> > + TP_PROTO(int *err),
> > +
> > + TP_ARGS(err),
> > +
> > + TP_STRUCT__entry(
> > + __field(int, err)
> > + ),
> > +
> > + TP_fast_assign(
> > + __entry->err = *err;
> > + ),
> > +
> > + TP_printk("bpf_test_finish with err=%d", __entry->err)
> > +);
> > +
> > +#ifdef DEFINE_EVENT_WRITABLE
> > +#undef BPF_TEST_RUN_DEFINE_EVENT
> > +#define BPF_TEST_RUN_DEFINE_EVENT(template, call, proto, args, size) \
> > + DEFINE_EVENT_WRITABLE(template, call, PARAMS(proto), \
> > + PARAMS(args), size)
> > +#else
> > +#undef BPF_TEST_RUN_DEFINE_EVENT
> > +#define BPF_TEST_RUN_DEFINE_EVENT(template, call, proto, args, size) \
> > + DEFINE_EVENT(template, call, PARAMS(proto), PARAMS(args))
> > +#endif
> > +
> > +BPF_TEST_RUN_DEFINE_EVENT(bpf_test_finish, bpf_test_finish,
> > +
> > + TP_PROTO(int *err),
> > +
> > + TP_ARGS(err),
> > +
> > + sizeof(int)
> > +);
> > +
> > +#endif
> > +
> > +/* This part must be outside protection */
> > +#include <trace/define_trace.h>
> > diff --git a/net/bpf/test_run.c b/net/bpf/test_run.c
> > index fab142b796ef..25e757102595 100644
> > --- a/net/bpf/test_run.c
> > +++ b/net/bpf/test_run.c
> > @@ -13,6 +13,9 @@
> > #include <net/sock.h>
> > #include <net/tcp.h>
> >
> > +#define CREATE_TRACE_POINTS
> > +#include <trace/events/bpf_test_run.h>
> > +
> > static int bpf_test_run(struct bpf_prog *prog, void *ctx, u32 repeat,
> > u32 *retval, u32 *time)
> > {
> > @@ -100,6 +103,7 @@ static int bpf_test_finish(const union bpf_attr *kattr,
> > if (err != -ENOSPC)
> > err = 0;
> > out:
> > + trace_bpf_test_finish(&err);
> > return err;
> > }
> >
> > diff --git a/tools/testing/selftests/bpf/prog_tests/raw_tp_writable_reject_nbd_invalid.c b/tools/testing/selftests/bpf/prog_tests/raw_tp_writable_reject_nbd_invalid.c
> > new file mode 100644
> > index 000000000000..328d5c4b084b
> > --- /dev/null
> > +++ b/tools/testing/selftests/bpf/prog_tests/raw_tp_writable_reject_nbd_invalid.c
> > @@ -0,0 +1,40 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +
> > +#include <test_progs.h>
> > +#include <linux/nbd.h>
> > +
> > +void test_raw_tp_writable_reject_nbd_invalid(void)
> > +{
> > + __u32 duration = 0;
> > + char error[4096];
> > + int bpf_fd = -1, tp_fd = -1;
> > +
> > + const struct bpf_insn program[] = {
> > + /* r6 is our tp buffer */
> > + BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, 0),
> > + BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_6, 128),
>
> The number "128" is a little cryptic. Maybe you can use something like
> sizeof(struct nbd_request)?
That was explicitly chosen to be (far) larger than an nbd_request, as
this program should be rejected by the verifier. If you really want, I
can do `sizeof(struct nbd_request) + some constant` and add a comment.
But the size of an nbd request should never change, as that's a network
protocol.
>
> > + BPF_EXIT_INSN(),
> > + };
> > +
> > + struct bpf_load_program_attr load_attr = {
> > + .prog_type = BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE,
> > + .license = "GPL v2",
> > + .insns = program,
> > + .insns_cnt = sizeof(program) / sizeof(struct bpf_insn),
> > + .log_level = 2,
> > + };
> > +
> > + bpf_fd = bpf_load_program_xattr(&load_attr, error, sizeof(error));
> > + if (CHECK(bpf_fd < 0, "bpf_raw_tracepoint_writable loaded",
> > + "failed: %d errno %d\n", bpf_fd, errno))
> > + return;
> > +
> > + tp_fd = bpf_raw_tracepoint_open("nbd_send_request", bpf_fd);
> > + if (CHECK(tp_fd >= 0, "bpf_raw_tracepoint_writable opened",
> > + "erroneously succeeded\n"))
> > + goto out_bpffd;
> > +
> > + close(tp_fd);
> > +out_bpffd:
> > + close(bpf_fd);
> > +}
> > diff --git a/tools/testing/selftests/bpf/prog_tests/raw_tp_writable_test_run.c b/tools/testing/selftests/bpf/prog_tests/raw_tp_writable_test_run.c
> > new file mode 100644
> > index 000000000000..4145925f9cab
> > --- /dev/null
> > +++ b/tools/testing/selftests/bpf/prog_tests/raw_tp_writable_test_run.c
> > @@ -0,0 +1,80 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +
> > +#include <test_progs.h>
> > +#include <linux/nbd.h>
> > +
> > +void test_raw_tp_writable_test_run(void)
> > +{
> > + __u32 duration = 0;
> > + char error[4096];
> > +
> > + const struct bpf_insn trace_program[] = {
> > + BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, 0),
> > + BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_6, 0),
> > + BPF_LD_IMM64(BPF_REG_0, 42),
>
> You can use BPF_MOV64_IMM(BPF_REG_0, 42) instead of BPF_LD_IMM64.
> BPF_LD_IMM64 is fine, but probably BPF_MOV64_IMM is better.
> The same for a few below instances.
Ah, right. I don't need the second opcode if the value can be zero-
extended.
>
> > + BPF_STX_MEM(BPF_W, BPF_REG_6, BPF_REG_0, 0),
> > + BPF_EXIT_INSN(),
> > + };
> > +
> > + struct bpf_load_program_attr load_attr = {
> > + .prog_type = BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE,
> > + .license = "GPL v2",
> > + .insns = trace_program,
> > + .insns_cnt = sizeof(trace_program) / sizeof(struct bpf_insn),
> > + .log_level = 2,
> > + };
> > +
> > + int bpf_fd = bpf_load_program_xattr(&load_attr, error, sizeof(error));
> > + if (CHECK(bpf_fd < 0, "bpf_raw_tracepoint_writable loaded",
> > + "failed: %d errno %d\n", bpf_fd, errno))
> > + return;
> > +
> > + const struct bpf_insn skb_program[] = {
> > + BPF_LD_IMM64(BPF_REG_0, 0),
> > + BPF_EXIT_INSN(),
> > + };
> > +
> > + struct bpf_load_program_attr skb_load_attr = {
> > + .prog_type = BPF_PROG_TYPE_SOCKET_FILTER,
> > + .license = "GPL v2",
> > + .insns = skb_program,
> > + .insns_cnt = sizeof(skb_program) / sizeof(struct bpf_insn),
> > + };
> > +
> > + int filter_fd =
> > + bpf_load_program_xattr(&skb_load_attr, error, sizeof(error));
> > + if (CHECK(filter_fd < 0, "test_program_loaded", "failed: %d errno %d\n",
> > + filter_fd, errno))
> > + goto out_bpffd;
> > +
> > + int tp_fd = bpf_raw_tracepoint_open("bpf_test_finish", bpf_fd);
> > + if (CHECK(tp_fd < 0, "bpf_raw_tracepoint_writable opened",
> > + "failed: %d errno %d\n", tp_fd, errno))
> > + goto out_filterfd;
> > +
> > + char test_skb[128] = {
> > + 0,
> > + };
> > +
> > + __u32 prog_ret;
> > + int err = bpf_prog_test_run(filter_fd, 1, test_skb, sizeof(test_skb), 0,
> > + 0, &prog_ret, 0);
> > + CHECK(err != 42, "test_run",
> > + "tracepoint did not modify return value\n");
> > + CHECK(prog_ret != 0, "test_run_ret",
> > + "socket_filter did not return 0\n");
> > +
> > + close(tp_fd);
> > +
> > + err = bpf_prog_test_run(filter_fd, 1, test_skb, sizeof(test_skb), 0, 0,
> > + &prog_ret, 0);
> > + CHECK(err != 0, "test_run_notrace",
> > + "test_run failed with %d errno %d\n", err, errno);
> > + CHECK(prog_ret != 0, "test_run_ret_notrace",
> > + "socket_filter did not return 0\n");
> > +
> > +out_filterfd:
> > + close(filter_fd);
> > +out_bpffd:
> > + close(bpf_fd);
> > +}
> > diff --git a/tools/testing/selftests/bpf/verifier/raw_tp_writable.c b/tools/testing/selftests/bpf/verifier/raw_tp_writable.c
> > new file mode 100644
> > index 000000000000..95b5d70a1dc1
> > --- /dev/null
> > +++ b/tools/testing/selftests/bpf/verifier/raw_tp_writable.c
> > @@ -0,0 +1,34 @@
> > +{
> > + "raw_tracepoint_writable: reject variable offset",
> > + .insns = {
> > + /* r6 is our tp buffer */
> > + BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, 0),
> > +
> > + BPF_LD_MAP_FD(BPF_REG_1, 0),
> > + /* move the key (== 0) to r10-8 */
> > + BPF_MOV32_IMM(BPF_REG_0, 0),
> > + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
> > + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
> > + BPF_STX_MEM(BPF_DW, BPF_REG_2, BPF_REG_0, 0),
> > + /* lookup in the map */
> > + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
> > + BPF_FUNC_map_lookup_elem),
> > +
> > + /* exit clean if null */
> > + BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
> > + BPF_EXIT_INSN(),
> > +
> > + /* shift the buffer pointer to a variable location */
> > + BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_0, 0),
> > + BPF_ALU64_REG(BPF_ADD, BPF_REG_6, BPF_REG_0),
> > + /* clobber whatever's there */
> > + BPF_MOV64_IMM(BPF_REG_7, 4242),
> > + BPF_STX_MEM(BPF_DW, BPF_REG_6, BPF_REG_7, 0),
> > +
> > + BPF_MOV64_IMM(BPF_REG_0, 0),
> > + BPF_EXIT_INSN(),
> > + },
> > + .fixup_map_hash_8b = { 1, },
> > + .prog_type = BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE,
> > + .errstr = "R6 invalid variable buffer offset: off=0, var_off=(0x0; 0xffffffff)",
> > +},
> >
WARNING: multiple messages have this Message-ID (diff)
From: mmullins at fb.com (Matt Mullins)
Subject: [PATCH bpf-next v3 5/5] selftests: bpf: test writable buffers in raw tps
Date: Mon, 22 Apr 2019 19:27:02 +0000 [thread overview]
Message-ID: <7a40c2851cd6f708a11cd003a05ab79c8644b10d.camel@fb.com> (raw)
In-Reply-To: <7f32dd3c-a1e1-2965-3a98-3fa9e54e8501@fb.com>
On Mon, 2019-04-22 at 18:32 +0000, Yonghong Song wrote:
>
> On 4/19/19 2:04 PM, Matt Mullins wrote:
> > This tests that:
> > * a BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE cannot be attached if it
> > uses either:
> > * a variable offset to the tracepoint buffer, or
> > * an offset beyond the size of the tracepoint buffer
> > * a tracer can modify the buffer provided when attached to a writable
> > tracepoint in bpf_prog_test_run
> >
> > Signed-off-by: Matt Mullins <mmullins at fb.com>
> > ---
> > include/trace/events/bpf_test_run.h | 50 ++++++++++++
> > net/bpf/test_run.c | 4 +
> > .../raw_tp_writable_reject_nbd_invalid.c | 40 ++++++++++
> > .../bpf/prog_tests/raw_tp_writable_test_run.c | 80 +++++++++++++++++++
> > .../selftests/bpf/verifier/raw_tp_writable.c | 34 ++++++++
> > 5 files changed, 208 insertions(+)
> > create mode 100644 include/trace/events/bpf_test_run.h
> > create mode 100644 tools/testing/selftests/bpf/prog_tests/raw_tp_writable_reject_nbd_invalid.c
> > create mode 100644 tools/testing/selftests/bpf/prog_tests/raw_tp_writable_test_run.c
> > create mode 100644 tools/testing/selftests/bpf/verifier/raw_tp_writable.c
> >
> > diff --git a/include/trace/events/bpf_test_run.h b/include/trace/events/bpf_test_run.h
> > new file mode 100644
> > index 000000000000..abf466839ea4
> > --- /dev/null
> > +++ b/include/trace/events/bpf_test_run.h
> > @@ -0,0 +1,50 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +#undef TRACE_SYSTEM
> > +#define TRACE_SYSTEM bpf_test_run
> > +
> > +#if !defined(_TRACE_NBD_H) || defined(TRACE_HEADER_MULTI_READ)
> > +#define _TRACE_BPF_TEST_RUN_H
> > +
> > +#include <linux/tracepoint.h>
> > +
> > +DECLARE_EVENT_CLASS(bpf_test_finish,
> > +
> > + TP_PROTO(int *err),
> > +
> > + TP_ARGS(err),
> > +
> > + TP_STRUCT__entry(
> > + __field(int, err)
> > + ),
> > +
> > + TP_fast_assign(
> > + __entry->err = *err;
> > + ),
> > +
> > + TP_printk("bpf_test_finish with err=%d", __entry->err)
> > +);
> > +
> > +#ifdef DEFINE_EVENT_WRITABLE
> > +#undef BPF_TEST_RUN_DEFINE_EVENT
> > +#define BPF_TEST_RUN_DEFINE_EVENT(template, call, proto, args, size) \
> > + DEFINE_EVENT_WRITABLE(template, call, PARAMS(proto), \
> > + PARAMS(args), size)
> > +#else
> > +#undef BPF_TEST_RUN_DEFINE_EVENT
> > +#define BPF_TEST_RUN_DEFINE_EVENT(template, call, proto, args, size) \
> > + DEFINE_EVENT(template, call, PARAMS(proto), PARAMS(args))
> > +#endif
> > +
> > +BPF_TEST_RUN_DEFINE_EVENT(bpf_test_finish, bpf_test_finish,
> > +
> > + TP_PROTO(int *err),
> > +
> > + TP_ARGS(err),
> > +
> > + sizeof(int)
> > +);
> > +
> > +#endif
> > +
> > +/* This part must be outside protection */
> > +#include <trace/define_trace.h>
> > diff --git a/net/bpf/test_run.c b/net/bpf/test_run.c
> > index fab142b796ef..25e757102595 100644
> > --- a/net/bpf/test_run.c
> > +++ b/net/bpf/test_run.c
> > @@ -13,6 +13,9 @@
> > #include <net/sock.h>
> > #include <net/tcp.h>
> >
> > +#define CREATE_TRACE_POINTS
> > +#include <trace/events/bpf_test_run.h>
> > +
> > static int bpf_test_run(struct bpf_prog *prog, void *ctx, u32 repeat,
> > u32 *retval, u32 *time)
> > {
> > @@ -100,6 +103,7 @@ static int bpf_test_finish(const union bpf_attr *kattr,
> > if (err != -ENOSPC)
> > err = 0;
> > out:
> > + trace_bpf_test_finish(&err);
> > return err;
> > }
> >
> > diff --git a/tools/testing/selftests/bpf/prog_tests/raw_tp_writable_reject_nbd_invalid.c b/tools/testing/selftests/bpf/prog_tests/raw_tp_writable_reject_nbd_invalid.c
> > new file mode 100644
> > index 000000000000..328d5c4b084b
> > --- /dev/null
> > +++ b/tools/testing/selftests/bpf/prog_tests/raw_tp_writable_reject_nbd_invalid.c
> > @@ -0,0 +1,40 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +
> > +#include <test_progs.h>
> > +#include <linux/nbd.h>
> > +
> > +void test_raw_tp_writable_reject_nbd_invalid(void)
> > +{
> > + __u32 duration = 0;
> > + char error[4096];
> > + int bpf_fd = -1, tp_fd = -1;
> > +
> > + const struct bpf_insn program[] = {
> > + /* r6 is our tp buffer */
> > + BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, 0),
> > + BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_6, 128),
>
> The number "128" is a little cryptic. Maybe you can use something like
> sizeof(struct nbd_request)?
That was explicitly chosen to be (far) larger than an nbd_request, as
this program should be rejected by the verifier. If you really want, I
can do `sizeof(struct nbd_request) + some constant` and add a comment.
But the size of an nbd request should never change, as that's a network
protocol.
>
> > + BPF_EXIT_INSN(),
> > + };
> > +
> > + struct bpf_load_program_attr load_attr = {
> > + .prog_type = BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE,
> > + .license = "GPL v2",
> > + .insns = program,
> > + .insns_cnt = sizeof(program) / sizeof(struct bpf_insn),
> > + .log_level = 2,
> > + };
> > +
> > + bpf_fd = bpf_load_program_xattr(&load_attr, error, sizeof(error));
> > + if (CHECK(bpf_fd < 0, "bpf_raw_tracepoint_writable loaded",
> > + "failed: %d errno %d\n", bpf_fd, errno))
> > + return;
> > +
> > + tp_fd = bpf_raw_tracepoint_open("nbd_send_request", bpf_fd);
> > + if (CHECK(tp_fd >= 0, "bpf_raw_tracepoint_writable opened",
> > + "erroneously succeeded\n"))
> > + goto out_bpffd;
> > +
> > + close(tp_fd);
> > +out_bpffd:
> > + close(bpf_fd);
> > +}
> > diff --git a/tools/testing/selftests/bpf/prog_tests/raw_tp_writable_test_run.c b/tools/testing/selftests/bpf/prog_tests/raw_tp_writable_test_run.c
> > new file mode 100644
> > index 000000000000..4145925f9cab
> > --- /dev/null
> > +++ b/tools/testing/selftests/bpf/prog_tests/raw_tp_writable_test_run.c
> > @@ -0,0 +1,80 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +
> > +#include <test_progs.h>
> > +#include <linux/nbd.h>
> > +
> > +void test_raw_tp_writable_test_run(void)
> > +{
> > + __u32 duration = 0;
> > + char error[4096];
> > +
> > + const struct bpf_insn trace_program[] = {
> > + BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, 0),
> > + BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_6, 0),
> > + BPF_LD_IMM64(BPF_REG_0, 42),
>
> You can use BPF_MOV64_IMM(BPF_REG_0, 42) instead of BPF_LD_IMM64.
> BPF_LD_IMM64 is fine, but probably BPF_MOV64_IMM is better.
> The same for a few below instances.
Ah, right. I don't need the second opcode if the value can be zero-
extended.
>
> > + BPF_STX_MEM(BPF_W, BPF_REG_6, BPF_REG_0, 0),
> > + BPF_EXIT_INSN(),
> > + };
> > +
> > + struct bpf_load_program_attr load_attr = {
> > + .prog_type = BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE,
> > + .license = "GPL v2",
> > + .insns = trace_program,
> > + .insns_cnt = sizeof(trace_program) / sizeof(struct bpf_insn),
> > + .log_level = 2,
> > + };
> > +
> > + int bpf_fd = bpf_load_program_xattr(&load_attr, error, sizeof(error));
> > + if (CHECK(bpf_fd < 0, "bpf_raw_tracepoint_writable loaded",
> > + "failed: %d errno %d\n", bpf_fd, errno))
> > + return;
> > +
> > + const struct bpf_insn skb_program[] = {
> > + BPF_LD_IMM64(BPF_REG_0, 0),
> > + BPF_EXIT_INSN(),
> > + };
> > +
> > + struct bpf_load_program_attr skb_load_attr = {
> > + .prog_type = BPF_PROG_TYPE_SOCKET_FILTER,
> > + .license = "GPL v2",
> > + .insns = skb_program,
> > + .insns_cnt = sizeof(skb_program) / sizeof(struct bpf_insn),
> > + };
> > +
> > + int filter_fd =
> > + bpf_load_program_xattr(&skb_load_attr, error, sizeof(error));
> > + if (CHECK(filter_fd < 0, "test_program_loaded", "failed: %d errno %d\n",
> > + filter_fd, errno))
> > + goto out_bpffd;
> > +
> > + int tp_fd = bpf_raw_tracepoint_open("bpf_test_finish", bpf_fd);
> > + if (CHECK(tp_fd < 0, "bpf_raw_tracepoint_writable opened",
> > + "failed: %d errno %d\n", tp_fd, errno))
> > + goto out_filterfd;
> > +
> > + char test_skb[128] = {
> > + 0,
> > + };
> > +
> > + __u32 prog_ret;
> > + int err = bpf_prog_test_run(filter_fd, 1, test_skb, sizeof(test_skb), 0,
> > + 0, &prog_ret, 0);
> > + CHECK(err != 42, "test_run",
> > + "tracepoint did not modify return value\n");
> > + CHECK(prog_ret != 0, "test_run_ret",
> > + "socket_filter did not return 0\n");
> > +
> > + close(tp_fd);
> > +
> > + err = bpf_prog_test_run(filter_fd, 1, test_skb, sizeof(test_skb), 0, 0,
> > + &prog_ret, 0);
> > + CHECK(err != 0, "test_run_notrace",
> > + "test_run failed with %d errno %d\n", err, errno);
> > + CHECK(prog_ret != 0, "test_run_ret_notrace",
> > + "socket_filter did not return 0\n");
> > +
> > +out_filterfd:
> > + close(filter_fd);
> > +out_bpffd:
> > + close(bpf_fd);
> > +}
> > diff --git a/tools/testing/selftests/bpf/verifier/raw_tp_writable.c b/tools/testing/selftests/bpf/verifier/raw_tp_writable.c
> > new file mode 100644
> > index 000000000000..95b5d70a1dc1
> > --- /dev/null
> > +++ b/tools/testing/selftests/bpf/verifier/raw_tp_writable.c
> > @@ -0,0 +1,34 @@
> > +{
> > + "raw_tracepoint_writable: reject variable offset",
> > + .insns = {
> > + /* r6 is our tp buffer */
> > + BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, 0),
> > +
> > + BPF_LD_MAP_FD(BPF_REG_1, 0),
> > + /* move the key (== 0) to r10-8 */
> > + BPF_MOV32_IMM(BPF_REG_0, 0),
> > + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
> > + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
> > + BPF_STX_MEM(BPF_DW, BPF_REG_2, BPF_REG_0, 0),
> > + /* lookup in the map */
> > + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
> > + BPF_FUNC_map_lookup_elem),
> > +
> > + /* exit clean if null */
> > + BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
> > + BPF_EXIT_INSN(),
> > +
> > + /* shift the buffer pointer to a variable location */
> > + BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_0, 0),
> > + BPF_ALU64_REG(BPF_ADD, BPF_REG_6, BPF_REG_0),
> > + /* clobber whatever's there */
> > + BPF_MOV64_IMM(BPF_REG_7, 4242),
> > + BPF_STX_MEM(BPF_DW, BPF_REG_6, BPF_REG_7, 0),
> > +
> > + BPF_MOV64_IMM(BPF_REG_0, 0),
> > + BPF_EXIT_INSN(),
> > + },
> > + .fixup_map_hash_8b = { 1, },
> > + .prog_type = BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE,
> > + .errstr = "R6 invalid variable buffer offset: off=0, var_off=(0x0; 0xffffffff)",
> > +},
> >
WARNING: multiple messages have this Message-ID (diff)
From: mmullins@fb.com (Matt Mullins)
Subject: [PATCH bpf-next v3 5/5] selftests: bpf: test writable buffers in raw tps
Date: Mon, 22 Apr 2019 19:27:02 +0000 [thread overview]
Message-ID: <7a40c2851cd6f708a11cd003a05ab79c8644b10d.camel@fb.com> (raw)
Message-ID: <20190422192702.MtR1vO5a0hiW9E5Y1AgNTwWt959ydEEwFr7EmdoNW_A@z> (raw)
In-Reply-To: <7f32dd3c-a1e1-2965-3a98-3fa9e54e8501@fb.com>
On Mon, 2019-04-22@18:32 +0000, Yonghong Song wrote:
>
> On 4/19/19 2:04 PM, Matt Mullins wrote:
> > This tests that:
> > * a BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE cannot be attached if it
> > uses either:
> > * a variable offset to the tracepoint buffer, or
> > * an offset beyond the size of the tracepoint buffer
> > * a tracer can modify the buffer provided when attached to a writable
> > tracepoint in bpf_prog_test_run
> >
> > Signed-off-by: Matt Mullins <mmullins at fb.com>
> > ---
> > include/trace/events/bpf_test_run.h | 50 ++++++++++++
> > net/bpf/test_run.c | 4 +
> > .../raw_tp_writable_reject_nbd_invalid.c | 40 ++++++++++
> > .../bpf/prog_tests/raw_tp_writable_test_run.c | 80 +++++++++++++++++++
> > .../selftests/bpf/verifier/raw_tp_writable.c | 34 ++++++++
> > 5 files changed, 208 insertions(+)
> > create mode 100644 include/trace/events/bpf_test_run.h
> > create mode 100644 tools/testing/selftests/bpf/prog_tests/raw_tp_writable_reject_nbd_invalid.c
> > create mode 100644 tools/testing/selftests/bpf/prog_tests/raw_tp_writable_test_run.c
> > create mode 100644 tools/testing/selftests/bpf/verifier/raw_tp_writable.c
> >
> > diff --git a/include/trace/events/bpf_test_run.h b/include/trace/events/bpf_test_run.h
> > new file mode 100644
> > index 000000000000..abf466839ea4
> > --- /dev/null
> > +++ b/include/trace/events/bpf_test_run.h
> > @@ -0,0 +1,50 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +#undef TRACE_SYSTEM
> > +#define TRACE_SYSTEM bpf_test_run
> > +
> > +#if !defined(_TRACE_NBD_H) || defined(TRACE_HEADER_MULTI_READ)
> > +#define _TRACE_BPF_TEST_RUN_H
> > +
> > +#include <linux/tracepoint.h>
> > +
> > +DECLARE_EVENT_CLASS(bpf_test_finish,
> > +
> > + TP_PROTO(int *err),
> > +
> > + TP_ARGS(err),
> > +
> > + TP_STRUCT__entry(
> > + __field(int, err)
> > + ),
> > +
> > + TP_fast_assign(
> > + __entry->err = *err;
> > + ),
> > +
> > + TP_printk("bpf_test_finish with err=%d", __entry->err)
> > +);
> > +
> > +#ifdef DEFINE_EVENT_WRITABLE
> > +#undef BPF_TEST_RUN_DEFINE_EVENT
> > +#define BPF_TEST_RUN_DEFINE_EVENT(template, call, proto, args, size) \
> > + DEFINE_EVENT_WRITABLE(template, call, PARAMS(proto), \
> > + PARAMS(args), size)
> > +#else
> > +#undef BPF_TEST_RUN_DEFINE_EVENT
> > +#define BPF_TEST_RUN_DEFINE_EVENT(template, call, proto, args, size) \
> > + DEFINE_EVENT(template, call, PARAMS(proto), PARAMS(args))
> > +#endif
> > +
> > +BPF_TEST_RUN_DEFINE_EVENT(bpf_test_finish, bpf_test_finish,
> > +
> > + TP_PROTO(int *err),
> > +
> > + TP_ARGS(err),
> > +
> > + sizeof(int)
> > +);
> > +
> > +#endif
> > +
> > +/* This part must be outside protection */
> > +#include <trace/define_trace.h>
> > diff --git a/net/bpf/test_run.c b/net/bpf/test_run.c
> > index fab142b796ef..25e757102595 100644
> > --- a/net/bpf/test_run.c
> > +++ b/net/bpf/test_run.c
> > @@ -13,6 +13,9 @@
> > #include <net/sock.h>
> > #include <net/tcp.h>
> >
> > +#define CREATE_TRACE_POINTS
> > +#include <trace/events/bpf_test_run.h>
> > +
> > static int bpf_test_run(struct bpf_prog *prog, void *ctx, u32 repeat,
> > u32 *retval, u32 *time)
> > {
> > @@ -100,6 +103,7 @@ static int bpf_test_finish(const union bpf_attr *kattr,
> > if (err != -ENOSPC)
> > err = 0;
> > out:
> > + trace_bpf_test_finish(&err);
> > return err;
> > }
> >
> > diff --git a/tools/testing/selftests/bpf/prog_tests/raw_tp_writable_reject_nbd_invalid.c b/tools/testing/selftests/bpf/prog_tests/raw_tp_writable_reject_nbd_invalid.c
> > new file mode 100644
> > index 000000000000..328d5c4b084b
> > --- /dev/null
> > +++ b/tools/testing/selftests/bpf/prog_tests/raw_tp_writable_reject_nbd_invalid.c
> > @@ -0,0 +1,40 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +
> > +#include <test_progs.h>
> > +#include <linux/nbd.h>
> > +
> > +void test_raw_tp_writable_reject_nbd_invalid(void)
> > +{
> > + __u32 duration = 0;
> > + char error[4096];
> > + int bpf_fd = -1, tp_fd = -1;
> > +
> > + const struct bpf_insn program[] = {
> > + /* r6 is our tp buffer */
> > + BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, 0),
> > + BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_6, 128),
>
> The number "128" is a little cryptic. Maybe you can use something like
> sizeof(struct nbd_request)?
That was explicitly chosen to be (far) larger than an nbd_request, as
this program should be rejected by the verifier. If you really want, I
can do `sizeof(struct nbd_request) + some constant` and add a comment.
But the size of an nbd request should never change, as that's a network
protocol.
>
> > + BPF_EXIT_INSN(),
> > + };
> > +
> > + struct bpf_load_program_attr load_attr = {
> > + .prog_type = BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE,
> > + .license = "GPL v2",
> > + .insns = program,
> > + .insns_cnt = sizeof(program) / sizeof(struct bpf_insn),
> > + .log_level = 2,
> > + };
> > +
> > + bpf_fd = bpf_load_program_xattr(&load_attr, error, sizeof(error));
> > + if (CHECK(bpf_fd < 0, "bpf_raw_tracepoint_writable loaded",
> > + "failed: %d errno %d\n", bpf_fd, errno))
> > + return;
> > +
> > + tp_fd = bpf_raw_tracepoint_open("nbd_send_request", bpf_fd);
> > + if (CHECK(tp_fd >= 0, "bpf_raw_tracepoint_writable opened",
> > + "erroneously succeeded\n"))
> > + goto out_bpffd;
> > +
> > + close(tp_fd);
> > +out_bpffd:
> > + close(bpf_fd);
> > +}
> > diff --git a/tools/testing/selftests/bpf/prog_tests/raw_tp_writable_test_run.c b/tools/testing/selftests/bpf/prog_tests/raw_tp_writable_test_run.c
> > new file mode 100644
> > index 000000000000..4145925f9cab
> > --- /dev/null
> > +++ b/tools/testing/selftests/bpf/prog_tests/raw_tp_writable_test_run.c
> > @@ -0,0 +1,80 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +
> > +#include <test_progs.h>
> > +#include <linux/nbd.h>
> > +
> > +void test_raw_tp_writable_test_run(void)
> > +{
> > + __u32 duration = 0;
> > + char error[4096];
> > +
> > + const struct bpf_insn trace_program[] = {
> > + BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, 0),
> > + BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_6, 0),
> > + BPF_LD_IMM64(BPF_REG_0, 42),
>
> You can use BPF_MOV64_IMM(BPF_REG_0, 42) instead of BPF_LD_IMM64.
> BPF_LD_IMM64 is fine, but probably BPF_MOV64_IMM is better.
> The same for a few below instances.
Ah, right. I don't need the second opcode if the value can be zero-
extended.
>
> > + BPF_STX_MEM(BPF_W, BPF_REG_6, BPF_REG_0, 0),
> > + BPF_EXIT_INSN(),
> > + };
> > +
> > + struct bpf_load_program_attr load_attr = {
> > + .prog_type = BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE,
> > + .license = "GPL v2",
> > + .insns = trace_program,
> > + .insns_cnt = sizeof(trace_program) / sizeof(struct bpf_insn),
> > + .log_level = 2,
> > + };
> > +
> > + int bpf_fd = bpf_load_program_xattr(&load_attr, error, sizeof(error));
> > + if (CHECK(bpf_fd < 0, "bpf_raw_tracepoint_writable loaded",
> > + "failed: %d errno %d\n", bpf_fd, errno))
> > + return;
> > +
> > + const struct bpf_insn skb_program[] = {
> > + BPF_LD_IMM64(BPF_REG_0, 0),
> > + BPF_EXIT_INSN(),
> > + };
> > +
> > + struct bpf_load_program_attr skb_load_attr = {
> > + .prog_type = BPF_PROG_TYPE_SOCKET_FILTER,
> > + .license = "GPL v2",
> > + .insns = skb_program,
> > + .insns_cnt = sizeof(skb_program) / sizeof(struct bpf_insn),
> > + };
> > +
> > + int filter_fd =
> > + bpf_load_program_xattr(&skb_load_attr, error, sizeof(error));
> > + if (CHECK(filter_fd < 0, "test_program_loaded", "failed: %d errno %d\n",
> > + filter_fd, errno))
> > + goto out_bpffd;
> > +
> > + int tp_fd = bpf_raw_tracepoint_open("bpf_test_finish", bpf_fd);
> > + if (CHECK(tp_fd < 0, "bpf_raw_tracepoint_writable opened",
> > + "failed: %d errno %d\n", tp_fd, errno))
> > + goto out_filterfd;
> > +
> > + char test_skb[128] = {
> > + 0,
> > + };
> > +
> > + __u32 prog_ret;
> > + int err = bpf_prog_test_run(filter_fd, 1, test_skb, sizeof(test_skb), 0,
> > + 0, &prog_ret, 0);
> > + CHECK(err != 42, "test_run",
> > + "tracepoint did not modify return value\n");
> > + CHECK(prog_ret != 0, "test_run_ret",
> > + "socket_filter did not return 0\n");
> > +
> > + close(tp_fd);
> > +
> > + err = bpf_prog_test_run(filter_fd, 1, test_skb, sizeof(test_skb), 0, 0,
> > + &prog_ret, 0);
> > + CHECK(err != 0, "test_run_notrace",
> > + "test_run failed with %d errno %d\n", err, errno);
> > + CHECK(prog_ret != 0, "test_run_ret_notrace",
> > + "socket_filter did not return 0\n");
> > +
> > +out_filterfd:
> > + close(filter_fd);
> > +out_bpffd:
> > + close(bpf_fd);
> > +}
> > diff --git a/tools/testing/selftests/bpf/verifier/raw_tp_writable.c b/tools/testing/selftests/bpf/verifier/raw_tp_writable.c
> > new file mode 100644
> > index 000000000000..95b5d70a1dc1
> > --- /dev/null
> > +++ b/tools/testing/selftests/bpf/verifier/raw_tp_writable.c
> > @@ -0,0 +1,34 @@
> > +{
> > + "raw_tracepoint_writable: reject variable offset",
> > + .insns = {
> > + /* r6 is our tp buffer */
> > + BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, 0),
> > +
> > + BPF_LD_MAP_FD(BPF_REG_1, 0),
> > + /* move the key (== 0) to r10-8 */
> > + BPF_MOV32_IMM(BPF_REG_0, 0),
> > + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
> > + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
> > + BPF_STX_MEM(BPF_DW, BPF_REG_2, BPF_REG_0, 0),
> > + /* lookup in the map */
> > + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
> > + BPF_FUNC_map_lookup_elem),
> > +
> > + /* exit clean if null */
> > + BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
> > + BPF_EXIT_INSN(),
> > +
> > + /* shift the buffer pointer to a variable location */
> > + BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_0, 0),
> > + BPF_ALU64_REG(BPF_ADD, BPF_REG_6, BPF_REG_0),
> > + /* clobber whatever's there */
> > + BPF_MOV64_IMM(BPF_REG_7, 4242),
> > + BPF_STX_MEM(BPF_DW, BPF_REG_6, BPF_REG_7, 0),
> > +
> > + BPF_MOV64_IMM(BPF_REG_0, 0),
> > + BPF_EXIT_INSN(),
> > + },
> > + .fixup_map_hash_8b = { 1, },
> > + .prog_type = BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE,
> > + .errstr = "R6 invalid variable buffer offset: off=0, var_off=(0x0; 0xffffffff)",
> > +},
> >
next prev parent reply other threads:[~2019-04-22 19:28 UTC|newest]
Thread overview: 24+ messages / expand[flat|nested] mbox.gz Atom feed top
2019-04-19 21:04 [PATCH bpf-next v3 0/5] writable contexts for bpf raw tracepoints Matt Mullins
2019-04-19 21:04 ` [PATCH bpf-next v3 1/5] bpf: add writable context for " Matt Mullins
2019-04-22 18:12 ` Yonghong Song
2019-04-22 19:23 ` Matt Mullins
2019-04-22 21:17 ` Yonghong Song
2019-04-22 23:01 ` Matt Mullins
2019-04-22 23:16 ` Yonghong Song
2019-04-19 21:04 ` [PATCH bpf-next v3 2/5] nbd: trace sending nbd requests Matt Mullins
2019-04-19 21:16 ` Josef Bacik
2019-04-19 21:04 ` [PATCH bpf-next v3 3/5] nbd: add tracepoints for send/receive timing Matt Mullins
2019-04-19 21:16 ` Josef Bacik
2019-04-19 21:04 ` [PATCH bpf-next v3 4/5] tools: sync bpf.h Matt Mullins
2019-04-19 21:04 ` [PATCH bpf-next v3 5/5] selftests: bpf: test writable buffers in raw tps Matt Mullins
2019-04-19 21:04 ` Matt Mullins
2019-04-19 21:04 ` mmullins
2019-04-22 18:32 ` Yonghong Song
2019-04-22 18:32 ` Yonghong Song
2019-04-22 18:32 ` yhs
2019-04-22 19:27 ` Matt Mullins [this message]
2019-04-22 19:27 ` Matt Mullins
2019-04-22 19:27 ` mmullins
2019-04-22 21:13 ` Yonghong Song
2019-04-22 21:13 ` Yonghong Song
2019-04-22 21:13 ` yhs
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=7a40c2851cd6f708a11cd003a05ab79c8644b10d.camel@fb.com \
--to=mmullins@fb.com \
--cc=ast@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=daniel@iogearbox.net \
--cc=davem@davemloft.net \
--cc=hall@fb.com \
--cc=kafai@fb.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-kselftest@vger.kernel.org \
--cc=mingo@redhat.com \
--cc=netdev@vger.kernel.org \
--cc=rostedt@goodmis.org \
--cc=shuah@kernel.org \
--cc=songliubraving@fb.com \
--cc=yhs@fb.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.