All of lore.kernel.org
 help / color / mirror / Atom feed
From: Roberto Sassu <roberto.sassu@huawei.com>
To: <ast@kernel.org>, <daniel@iogearbox.net>, <andrii@kernel.org>,
	<kpsingh@kernel.org>
Cc: <bpf@vger.kernel.org>, <netdev@vger.kernel.org>,
	<linux-kselftest@vger.kernel.org>, <linux-kernel@vger.kernel.org>,
	Roberto Sassu <roberto.sassu@huawei.com>
Subject: [PATCH 3/3] bpf: Add tests for signed map values
Date: Wed, 25 May 2022 15:21:15 +0200	[thread overview]
Message-ID: <20220525132115.896698-4-roberto.sassu@huawei.com> (raw)
In-Reply-To: <20220525132115.896698-1-roberto.sassu@huawei.com>

Check the ability of eBPF to restrict map update operations based on
signature verification of provided map values, if the map flag
BPF_F_VERIFY_ELEM is set.

Ensure that the map update operation is rejected if the signature is
invalid, or the data format is not correct. Also check that
bpf_map_verified_data_size() returns the correct data size for an added
signed map value.

Execute the test for a single element and in batch mode.

The test requires access to the kernel modules signing key and the
execution of the sign-file tool with the signing key path passed as
argument.

Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
---
 .../bpf/prog_tests/test_map_value_sig.c       | 212 ++++++++++++++++++
 .../selftests/bpf/progs/map_value_sig.c       |  50 +++++
 2 files changed, 262 insertions(+)
 create mode 100644 tools/testing/selftests/bpf/prog_tests/test_map_value_sig.c
 create mode 100644 tools/testing/selftests/bpf/progs/map_value_sig.c

diff --git a/tools/testing/selftests/bpf/prog_tests/test_map_value_sig.c b/tools/testing/selftests/bpf/prog_tests/test_map_value_sig.c
new file mode 100644
index 000000000000..0c74627f54c6
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/test_map_value_sig.c
@@ -0,0 +1,212 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Copyright (C) 2022 Huawei Technologies Duesseldorf GmbH
+ *
+ * Author: Roberto Sassu <roberto.sassu@huawei.com>
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <endian.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <test_progs.h>
+
+#include "map_value_sig.skel.h"
+
+#define MAX_DATA_SIZE 1024
+#define ARRAY_ELEMS 5
+
+struct data {
+	u8 payload[MAX_DATA_SIZE];
+};
+
+struct data_info {
+	char str[10];
+	int str_len;
+};
+
+int populate_data_item(struct data *data_item, struct data_info *data_info_item)
+{
+	struct stat st;
+	char signed_file_template[] = "/tmp/signed_fileXXXXXX";
+	int ret, fd, child_status, child_pid;
+
+	fd = mkstemp(signed_file_template);
+	if (fd == -1) {
+		ret = -errno;
+		goto out;
+	}
+
+	ret = write(fd, data_info_item->str, data_info_item->str_len);
+
+	close(fd);
+
+	if (ret != data_info_item->str_len) {
+		ret = -EIO;
+		goto out;
+	}
+
+	child_pid = fork();
+
+	if (child_pid == -1) {
+		ret = -errno;
+		goto out;
+	}
+
+	if (child_pid == 0)
+		return execlp("../../../../scripts/sign-file",
+			      "../../../../scripts/sign-file", "sha256",
+			      "../../../../certs/signing_key.pem",
+			      "../../../../certs/signing_key.pem",
+			      signed_file_template, NULL);
+
+	waitpid(child_pid, &child_status, 0);
+
+	ret = WEXITSTATUS(child_status);
+	if (ret)
+		goto out;
+
+	ret = stat(signed_file_template, &st);
+	if (ret == -1) {
+		ret = -errno;
+		goto out;
+	}
+
+	if (st.st_size > sizeof(data_item->payload) - sizeof(u32)) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	*(u32 *)data_item->payload = __cpu_to_be32(st.st_size);
+
+	fd = open(signed_file_template, O_RDONLY);
+	if (fd == -1) {
+		ret = -errno;
+		goto out;
+	}
+
+	ret = read(fd, data_item->payload + sizeof(u32), st.st_size);
+
+	close(fd);
+
+	if (ret != st.st_size) {
+		ret = -EIO;
+		goto out;
+	}
+
+	ret = 0;
+out:
+	unlink(signed_file_template);
+	return ret;
+}
+
+void test_test_map_value_sig(void)
+{
+	struct map_value_sig *skel = NULL;
+	struct bpf_map *map;
+	struct data *data_array = NULL;
+	struct data_info *data_info_array = NULL;
+	int keys[ARRAY_ELEMS];
+	u32 max_entries = ARRAY_ELEMS;
+	int ret, zero = 0, i, map_fd, duration = 0;
+
+	DECLARE_LIBBPF_OPTS(bpf_map_create_opts, create_opts,
+			    .map_flags = BPF_F_MMAPABLE | BPF_F_VERIFY_ELEM);
+
+	DECLARE_LIBBPF_OPTS(bpf_map_batch_opts, opts,
+		.elem_flags = 0,
+		.flags = 0,
+	);
+
+	data_array = malloc(sizeof(*data_array) * ARRAY_ELEMS);
+	if (CHECK(!data_array, "data array", "not enough memory\n"))
+		goto close_prog;
+
+	data_info_array = malloc(sizeof(*data_info_array) * ARRAY_ELEMS);
+	if (CHECK(!data_info_array, "data info array", "not enough memory\n"))
+		goto close_prog;
+
+	skel = map_value_sig__open_and_load();
+	if (CHECK(!skel, "skel", "open_and_load failed\n"))
+		goto close_prog;
+
+	ret = map_value_sig__attach(skel);
+	if (CHECK(ret < 0, "skel", "attach failed\n"))
+		goto close_prog;
+
+	map_fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, NULL, 4,
+				sizeof(struct data), ARRAY_ELEMS, &create_opts);
+	if (CHECK(map_fd != -EINVAL, "bpf_map_create",
+		  "should fail (mmapable & verify_elem flags set\n"))
+		goto close_prog;
+
+	map = bpf_object__find_map_by_name(skel->obj, "data_input");
+	if (CHECK(!map, "bpf_object__find_map_by_name", "not found\n"))
+		goto close_prog;
+
+	for (i = 0; i < ARRAY_ELEMS; i++) {
+		keys[i] = i;
+
+		data_info_array[i].str_len = snprintf(data_info_array[i].str,
+						 sizeof(data_info_array[i].str),
+						 "test%d", i);
+
+		ret = populate_data_item(&data_array[i], &data_info_array[i]);
+		if (CHECK(ret, "populate_data_item", "error: %d\n", ret))
+			goto close_prog;
+
+		ret = bpf_map_update_elem(bpf_map__fd(map), &zero,
+					  &data_array[i], BPF_ANY);
+		if (CHECK(ret < 0, "bpf_map_update_elem", "error: %d\n", ret))
+			goto close_prog;
+
+		if (CHECK(skel->bss->verified_data_size !=
+			  data_info_array[i].str_len, "data size",
+			  "mismatch\n"))
+			goto close_prog;
+	}
+
+	ret = bpf_map_update_batch(bpf_map__fd(map), keys, (void *)data_array,
+				   &max_entries, &opts);
+	if (CHECK(ret, "bpf_map_update_batch", "error: %d\n", ret))
+		goto close_prog;
+
+	*(u32 *)data_array[0].payload =
+				__cpu_to_be32(sizeof(data_array[0].payload));
+
+	ret = bpf_map_update_elem(bpf_map__fd(map), &zero, &data_array[0],
+				  BPF_ANY);
+	if (CHECK(!ret, "bpf_map_update_elem", "should fail (invalid size)\n"))
+		goto close_prog;
+
+	ret = bpf_map_update_batch(bpf_map__fd(map), keys, (void *)data_array,
+				   &max_entries, &opts);
+	if (CHECK(!ret, "bpf_map_update_batch", "should fail (invalid size)\n"))
+		goto close_prog;
+
+	*(u32 *)data_array[0].payload =
+				__cpu_to_be32(data_info_array[0].str_len);
+
+	data_array[0].payload[sizeof(u32) + data_info_array[0].str_len - 1] =
+									'\0';
+	ret = bpf_map_update_elem(bpf_map__fd(map), &zero, &data_array[0], 0);
+	if (CHECK(!ret, "bpf_map_update_elem",
+		  "should fail (invalid signature)\n"))
+		goto close_prog;
+
+	max_entries = ARRAY_ELEMS;
+
+	ret = bpf_map_update_batch(bpf_map__fd(map), keys, (void *)data_array,
+				   &max_entries, &opts);
+	if (CHECK(!ret, "bpf_map_update_batch",
+		  "should fail (invalid signature)\n"))
+		goto close_prog;
+close_prog:
+	map_value_sig__destroy(skel);
+	free(data_array);
+	free(data_info_array);
+}
diff --git a/tools/testing/selftests/bpf/progs/map_value_sig.c b/tools/testing/selftests/bpf/progs/map_value_sig.c
new file mode 100644
index 000000000000..a1d0fc021127
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/map_value_sig.c
@@ -0,0 +1,50 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Copyright (C) 2022 Huawei Technologies Duesseldorf GmbH
+ *
+ * Author: Roberto Sassu <roberto.sassu@huawei.com>
+ */
+
+#include "vmlinux.h"
+#include <errno.h>
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+
+#define MAX_DATA_SIZE 1024
+#define ARRAY_ELEMS 5
+
+u32 verified_data_size;
+
+struct data {
+	u8 payload[MAX_DATA_SIZE];
+};
+
+struct {
+	__uint(type, BPF_MAP_TYPE_ARRAY);
+	__uint(map_flags, BPF_F_VERIFY_ELEM);
+	__uint(max_entries, ARRAY_ELEMS);
+	__type(key, __u32);
+	__type(value, struct data);
+} data_input SEC(".maps");
+
+char _license[] SEC("license") = "GPL";
+
+SEC("fexit/array_map_update_elem")
+int BPF_PROG(array_map_update_elem, struct bpf_map *map, void *key, void *value,
+	     u64 map_flags)
+{
+	struct data *data_ptr;
+	int zero = 0;
+
+	if (map != (struct bpf_map *)&data_input)
+		return 0;
+
+	data_ptr = bpf_map_lookup_elem(&data_input, &zero);
+	if (!data_ptr)
+		return 0;
+
+	verified_data_size = bpf_map_verified_data_size((void *)data_ptr,
+							sizeof(struct data));
+	return 0;
+}
-- 
2.25.1


      parent reply	other threads:[~2022-05-25 13:22 UTC|newest]

Thread overview: 12+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-05-25 13:21 [PATCH 0/3] bpf: Add support for maps with authenticated values Roberto Sassu
2022-05-25 13:21 ` [PATCH 1/3] bpf: Add BPF_F_VERIFY_ELEM to require signature verification on map values Roberto Sassu
2022-05-25 16:51   ` kernel test robot
2022-05-25 18:50   ` kernel test robot
2022-05-25 22:53   ` kernel test robot
2022-06-03 12:07   ` KP Singh
2022-06-03 13:11     ` Roberto Sassu
2022-06-03 15:17       ` KP Singh
2022-06-03 15:43         ` Roberto Sassu
2022-06-04  9:32           ` Alexei Starovoitov
2022-05-25 13:21 ` [PATCH 2/3] bpf: Introduce bpf_map_verified_data_size() helper Roberto Sassu
2022-05-25 13:21 ` Roberto Sassu [this message]

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=20220525132115.896698-4-roberto.sassu@huawei.com \
    --to=roberto.sassu@huawei.com \
    --cc=andrii@kernel.org \
    --cc=ast@kernel.org \
    --cc=bpf@vger.kernel.org \
    --cc=daniel@iogearbox.net \
    --cc=kpsingh@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-kselftest@vger.kernel.org \
    --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.