All of lore.kernel.org
 help / color / mirror / Atom feed
From: Andrey Ignatov <rdna@fb.com>
To: <netdev@vger.kernel.org>
Cc: Andrey Ignatov <rdna@fb.com>, <ast@kernel.org>,
	<daniel@iogearbox.net>, <guro@fb.com>, <kernel-team@fb.com>
Subject: [PATCH bpf-next 21/21] selftests/bpf: C based test for sysctl and strtoX
Date: Sat, 23 Mar 2019 17:12:34 -0700	[thread overview]
Message-ID: <45316a240b8f3a6a048499aae672e429e76d851e.1553385599.git.rdna@fb.com> (raw)
In-Reply-To: <cover.1553385598.git.rdna@fb.com>

Add C based test for a few bpf_sysctl_* helpers and bpf_strtoul.

Make sure that sysctl can be identified by name and that multiple
integers can be parsed from sysctl value with bpf_strtoul.

net/ipv4/tcp_mem is chosen as a testing sysctl, it contains 3 unsigned
longs, they all are parsed and compared (val[0] < val[1] < val[2]).

One verifier limitation is being worked around in the test: variable
stack access. Since such an access is unsupported, offset, returned by
bpf_strtoul while parsing current ulong, can't be used directly when
specifying 'buf' pointer to the next bpf_strtoul. That's why instead of
'base + offset' where offset non-const tnum, the test uses brute force
search to find static number, that matches with 'offset', and use
'base + static' to access the stack.

Example of output:
  # ./test_sysctl
  ...
  Test case: C prog: deny all writes .. [PASS]
  Test case: C prog: deny access by name .. [PASS]
  Test case: C prog: read tcp_mem .. [PASS]
  Summary: 39 PASSED, 0 FAILED

Signed-off-by: Andrey Ignatov <rdna@fb.com>
---
 .../selftests/bpf/progs/test_sysctl_prog.c    | 85 +++++++++++++++++++
 tools/testing/selftests/bpf/test_sysctl.c     | 57 ++++++++++++-
 2 files changed, 141 insertions(+), 1 deletion(-)
 create mode 100644 tools/testing/selftests/bpf/progs/test_sysctl_prog.c

diff --git a/tools/testing/selftests/bpf/progs/test_sysctl_prog.c b/tools/testing/selftests/bpf/progs/test_sysctl_prog.c
new file mode 100644
index 000000000000..12d9def07a3e
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/test_sysctl_prog.c
@@ -0,0 +1,85 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2019 Facebook
+
+#include <stdint.h>
+#include <string.h>
+
+#include <linux/stddef.h>
+#include <linux/bpf.h>
+
+#include "bpf_helpers.h"
+
+/* Min/max supported lengths of a string with unsigned long in base 10. */
+#define MIN_ULONG_STR_LEN 2
+#define MAX_ULONG_STR_LEN 15
+
+static __always_inline int is_tcp_mem(struct bpf_sysctl *ctx)
+{
+	char tcp_mem_name[] = "net/ipv4/tcp_mem";
+	unsigned char i;
+	char name[64];
+	int ret;
+
+	memset(name, 0, sizeof(name));
+	ret = bpf_sysctl_get_name(ctx, name, sizeof(name), 0);
+	if (ret < 0 || ret != sizeof(tcp_mem_name) - 1)
+		return 0;
+
+#pragma clang loop unroll(full)
+	for (i = 0; i < sizeof(tcp_mem_name); ++i)
+		if (name[i] != tcp_mem_name[i])
+			return 0;
+
+	return 1;
+}
+
+SEC("cgroup/sysctl")
+int sysctl_tcp_mem(struct bpf_sysctl *ctx)
+{
+	unsigned long mem_min = 0, mem_pressure = 0, mem_max = 0;
+	unsigned char i, off;
+	char value[64];
+	int ret;
+
+	if (ctx->write)
+		return 0;
+
+	if (!is_tcp_mem(ctx))
+		return 0;
+
+	ret = bpf_sysctl_get_current_value(ctx, value, sizeof(value));
+	if (ret < 0 || ret >= sizeof(value))
+		return 0;
+
+	ret = bpf_strtoul(value, sizeof(value), 0, &mem_min);
+	if (ret <= 0 || ret > MAX_ULONG_STR_LEN)
+		return 0;
+	off = ret;
+	ret = -1;
+
+	/* Make verifier happy, prevent "invalid variable stack read R1
+	 * var_off=(0x0; 0xf)" by using static offset for value.
+	 */
+#pragma clang loop unroll(full)
+	for (i = MIN_ULONG_STR_LEN; i <= MAX_ULONG_STR_LEN; ++i)
+		if (off == i)
+			ret = bpf_strtoul(value + i, sizeof(value) - i, 0,
+					  &mem_pressure);
+	if (ret <= 0 || ret > MAX_ULONG_STR_LEN)
+		return 0;
+	off += ret;
+	ret = -1;
+
+	/* Same here. */
+#pragma clang loop unroll(full)
+	for (i = MIN_ULONG_STR_LEN * 2; i <= MAX_ULONG_STR_LEN * 2 + 2; ++i)
+		if (off == i)
+			ret = bpf_strtoul(value + i, sizeof(value) - i, 0,
+					  &mem_max);
+	if (ret <= 0 || ret > MAX_ULONG_STR_LEN)
+		return 0;
+
+	return mem_min < mem_pressure && mem_pressure < mem_max;
+}
+
+char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/test_sysctl.c b/tools/testing/selftests/bpf/test_sysctl.c
index 885675480af9..a3bebd7c68dd 100644
--- a/tools/testing/selftests/bpf/test_sysctl.c
+++ b/tools/testing/selftests/bpf/test_sysctl.c
@@ -11,6 +11,7 @@
 #include <linux/filter.h>
 
 #include <bpf/bpf.h>
+#include <bpf/libbpf.h>
 
 #include "bpf_rlimit.h"
 #include "bpf_util.h"
@@ -26,6 +27,7 @@ struct sysctl_test {
 	const char *descr;
 	size_t fixup_value_insn;
 	struct bpf_insn	insns[MAX_INSNS];
+	const char *prog_file;
 	enum bpf_attach_type attach_type;
 	const char *sysctl;
 	int open_flags;
@@ -1302,6 +1304,31 @@ static struct sysctl_test tests[] = {
 		.open_flags = O_RDONLY,
 		.result = SUCCESS,
 	},
+	{
+		"C prog: deny all writes",
+		.prog_file = "./test_sysctl_prog.o",
+		.attach_type = BPF_CGROUP_SYSCTL,
+		.sysctl = "net/ipv4/tcp_mem",
+		.open_flags = O_WRONLY,
+		.newval = "123 456 789",
+		.result = OP_EPERM,
+	},
+	{
+		"C prog: deny access by name",
+		.prog_file = "./test_sysctl_prog.o",
+		.attach_type = BPF_CGROUP_SYSCTL,
+		.sysctl = "net/ipv4/route/mtu_expires",
+		.open_flags = O_RDONLY,
+		.result = OP_EPERM,
+	},
+	{
+		"C prog: read tcp_mem",
+		.prog_file = "./test_sysctl_prog.o",
+		.attach_type = BPF_CGROUP_SYSCTL,
+		.sysctl = "net/ipv4/tcp_mem",
+		.open_flags = O_RDONLY,
+		.result = SUCCESS,
+	},
 };
 
 static size_t probe_prog_length(const struct bpf_insn *fp)
@@ -1335,7 +1362,8 @@ static int fixup_sysctl_value(const char *buf, size_t buf_len,
 	return 0;
 }
 
-static int load_sysctl_prog(struct sysctl_test *test, const char *sysctl_path)
+static int load_sysctl_prog_insns(struct sysctl_test *test,
+				  const char *sysctl_path)
 {
 	struct bpf_insn *prog = test->insns;
 	struct bpf_load_program_attr attr;
@@ -1377,6 +1405,33 @@ static int load_sysctl_prog(struct sysctl_test *test, const char *sysctl_path)
 	return ret;
 }
 
+static int load_sysctl_prog_file(struct sysctl_test *test)
+{
+	struct bpf_prog_load_attr attr;
+	struct bpf_object *obj;
+	int prog_fd;
+
+	memset(&attr, 0, sizeof(struct bpf_prog_load_attr));
+	attr.file = test->prog_file;
+	attr.prog_type = BPF_PROG_TYPE_CGROUP_SYSCTL;
+
+	if (bpf_prog_load_xattr(&attr, &obj, &prog_fd)) {
+		if (test->result != LOAD_REJECT)
+			log_err(">>> Loading program (%s) error.\n",
+				test->prog_file);
+		return -1;
+	}
+
+	return prog_fd;
+}
+
+static int load_sysctl_prog(struct sysctl_test *test, const char *sysctl_path)
+{
+		return test->prog_file
+			? load_sysctl_prog_file(test)
+			: load_sysctl_prog_insns(test, sysctl_path);
+}
+
 static int access_sysctl(const char *sysctl_path,
 			 const struct sysctl_test *test)
 {
-- 
2.17.1


  parent reply	other threads:[~2019-03-24  0:13 UTC|newest]

Thread overview: 24+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-03-24  0:12 [PATCH bpf-next 00/21] bpf: Sysctl hook Andrey Ignatov
2019-03-24  0:12 ` [PATCH bpf-next 01/21] bpf: Add base proto function for cgroup-bpf programs Andrey Ignatov
2019-03-24  0:12 ` [PATCH bpf-next 02/21] bpf: Sysctl hook Andrey Ignatov
2019-03-24  0:12 ` [PATCH bpf-next 03/21] bpf: Introduce bpf_sysctl_get_name helper Andrey Ignatov
2019-03-24  0:12 ` [PATCH bpf-next 04/21] bpf: Introduce bpf_sysctl_get_current_value helper Andrey Ignatov
2019-03-24  0:12 ` [PATCH bpf-next 05/21] bpf: Introduce bpf_sysctl_{get,set}_new_value helpers Andrey Ignatov
2019-03-24  0:12 ` [PATCH bpf-next 06/21] bpf: Add file_pos field to bpf_sysctl ctx Andrey Ignatov
2019-03-24  0:12 ` [PATCH bpf-next 07/21] bpf: Sync bpf.h to tools/ Andrey Ignatov
2019-03-24  0:12 ` [PATCH bpf-next 18/21] selftests/bpf: Add sysctl and strtoX helpers to bpf_helpers.h Andrey Ignatov
2019-03-24  0:12 ` [PATCH bpf-next 19/21] selftests/bpf: Test ARG_PTR_TO_LONG arg type Andrey Ignatov
2019-03-24  0:12 ` [PATCH bpf-next 20/21] selftests/bpf: Test bpf_strtol and bpf_strtoul helpers Andrey Ignatov
2019-03-24  0:12 ` Andrey Ignatov [this message]
2019-03-24  0:21 ` [PATCH bpf-next 08/21] libbpf: Support sysctl hook Andrey Ignatov
2019-03-24  0:21 ` [PATCH bpf-next 09/21] selftests/bpf: Test sysctl section name Andrey Ignatov
2019-03-24  0:23 ` [PATCH bpf-next 10/21] selftests/bpf: Test BPF_CGROUP_SYSCTL Andrey Ignatov
2019-03-24  0:23 ` [PATCH bpf-next 11/21] selftests/bpf: Test bpf_sysctl_get_name helper Andrey Ignatov
2019-03-24  0:23 ` [PATCH bpf-next 12/21] selftests/bpf: Test sysctl_get_current_value helper Andrey Ignatov
2019-03-24  0:23 ` [PATCH bpf-next 13/21] selftests/bpf: Test bpf_sysctl_{get,set}_new_value helpers Andrey Ignatov
2019-03-24  0:23 ` [PATCH bpf-next 14/21] selftests/bpf: Test file_pos field in bpf_sysctl ctx Andrey Ignatov
2019-03-24  0:23 ` [PATCH bpf-next 15/21] bpf: Introduce ARG_PTR_TO_{INT,LONG} arg types Andrey Ignatov
2019-03-24  0:23 ` [PATCH bpf-next 16/21] bpf: Introduce bpf_strtol and bpf_strtoul helpers Andrey Ignatov
2019-03-24  0:23 ` [PATCH bpf-next 17/21] bpf: Sync bpf.h to tools/ Andrey Ignatov
2019-03-25 10:27 ` [PATCH bpf-next 00/21] bpf: Sysctl hook Daniel Borkmann
2019-03-25 17:32   ` Andrey Ignatov

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=45316a240b8f3a6a048499aae672e429e76d851e.1553385599.git.rdna@fb.com \
    --to=rdna@fb.com \
    --cc=ast@kernel.org \
    --cc=daniel@iogearbox.net \
    --cc=guro@fb.com \
    --cc=kernel-team@fb.com \
    --cc=netdev@vger.kernel.org \
    /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.