From patchwork Wed May 25 13:21:13 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Roberto Sassu X-Patchwork-Id: 12861150 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id B3E1BC433EF for ; Wed, 25 May 2022 13:21:43 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S235264AbiEYNVm (ORCPT ); Wed, 25 May 2022 09:21:42 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:52056 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229576AbiEYNVl (ORCPT ); Wed, 25 May 2022 09:21:41 -0400 Received: from frasgout.his.huawei.com (frasgout.his.huawei.com [185.176.79.56]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 28F4327FF6; Wed, 25 May 2022 06:21:38 -0700 (PDT) Received: from fraeml714-chm.china.huawei.com (unknown [172.18.147.201]) by frasgout.his.huawei.com (SkyGuard) with ESMTP id 4L7Wss32wrz67j7Z; Wed, 25 May 2022 21:21:05 +0800 (CST) Received: from roberto-ThinkStation-P620.huawei.com (10.204.63.22) by fraeml714-chm.china.huawei.com (10.206.15.33) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2375.24; Wed, 25 May 2022 15:21:35 +0200 From: Roberto Sassu To: , , , CC: , , , , Roberto Sassu Subject: [PATCH 1/3] bpf: Add BPF_F_VERIFY_ELEM to require signature verification on map values Date: Wed, 25 May 2022 15:21:13 +0200 Message-ID: <20220525132115.896698-2-roberto.sassu@huawei.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20220525132115.896698-1-roberto.sassu@huawei.com> References: <20220525132115.896698-1-roberto.sassu@huawei.com> MIME-Version: 1.0 X-Originating-IP: [10.204.63.22] X-ClientProxiedBy: lhreml753-chm.china.huawei.com (10.201.108.203) To fraeml714-chm.china.huawei.com (10.206.15.33) X-CFilter-Loop: Reflected Precedence: bulk List-ID: X-Mailing-List: linux-kselftest@vger.kernel.org In some cases, it is desirable to ensure that a map contains data from authenticated sources, for example if map data are used for making security decisions. Such restriction is achieved by verifying the signature of map values, at the time those values are added to the map with the bpf() system call (more specifically, when the commands passed to bpf() are BPF_MAP_UPDATE_ELEM or BPF_MAP_UPDATE_BATCH). Mmappable maps are not allowed in this case. Signature verification is initially done with keys in the primary and secondary kernel keyrings, similarly to kernel modules. This allows system owners to enforce a system-wide policy based on the keys they trust. Support for additional keyrings could be added later, based on use case needs. Signature verification is done only for those maps for which the new map flag BPF_F_VERIFY_ELEM is set. When the flag is set, the kernel expects map values to be in the following format: +-------------------------------+---------------+-----+-----------------+ | verified data+sig size (be32) | verified data | sig | unverified data | +-------------------------------+---------------+-----+-----------------+ where sig is a module-style appended signature as generated by the sign-file tool. The verified data+sig size (in big endian) must be explicitly provided (it is not generated by sign-file), as it cannot be determined in other ways (currently, the map value size is fixed). It can be obtained from the size of the file created by sign-file. Introduce the new map flag BPF_F_VERIFY_ELEM, and additionally call the new function bpf_map_verify_value_sig() from bpf_map_update_value() if the flag is set. bpf_map_verify_value_sig(), declared as global for a new helper, is basically equivalent to mod_verify_sig(). It additionally does the marker check, that for kernel modules is done in module_sig_check(), and the parsing of the verified data+sig size. Currently, enable the usage of the flag only for the array map. Support for more map types can be added later. Signed-off-by: Roberto Sassu Reported-by: kernel test robot Reported-by: kernel test robot Reported-by: kernel test robot --- include/linux/bpf.h | 7 ++++ include/uapi/linux/bpf.h | 3 ++ kernel/bpf/arraymap.c | 2 +- kernel/bpf/syscall.c | 70 ++++++++++++++++++++++++++++++++++ tools/include/uapi/linux/bpf.h | 3 ++ 5 files changed, 84 insertions(+), 1 deletion(-) diff --git a/include/linux/bpf.h b/include/linux/bpf.h index a7080c86fa76..8f5c042e70a7 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -1825,6 +1825,8 @@ static inline bool unprivileged_ebpf_enabled(void) return !sysctl_unprivileged_bpf_disabled; } +int bpf_map_verify_value_sig(const void *mod, size_t modlen, bool verify); + #else /* !CONFIG_BPF_SYSCALL */ static inline struct bpf_prog *bpf_prog_get(u32 ufd) { @@ -2034,6 +2036,11 @@ static inline bool unprivileged_ebpf_enabled(void) return false; } +static inline int bpf_map_verify_value_sig(const void *mod, size_t modlen, + bool verify) +{ + return -EOPNOTSUPP; +} #endif /* CONFIG_BPF_SYSCALL */ void __bpf_free_used_btfs(struct bpf_prog_aux *aux, diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index f4009dbdf62d..a8e7803d2593 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -1226,6 +1226,9 @@ enum { /* Create a map that is suitable to be an inner map with dynamic max entries */ BPF_F_INNER_MAP = (1U << 12), + +/* Verify map value (fmt: ver data+sig size(be32), ver data, sig, unver data) */ + BPF_F_VERIFY_ELEM = (1U << 13) }; /* Flags for BPF_PROG_QUERY. */ diff --git a/kernel/bpf/arraymap.c b/kernel/bpf/arraymap.c index fe40d3b9458f..b430fdd0e892 100644 --- a/kernel/bpf/arraymap.c +++ b/kernel/bpf/arraymap.c @@ -17,7 +17,7 @@ #define ARRAY_CREATE_FLAG_MASK \ (BPF_F_NUMA_NODE | BPF_F_MMAPABLE | BPF_F_ACCESS_MASK | \ - BPF_F_PRESERVE_ELEMS | BPF_F_INNER_MAP) + BPF_F_PRESERVE_ELEMS | BPF_F_INNER_MAP | BPF_F_VERIFY_ELEM) static void bpf_array_free_percpu(struct bpf_array *array) { diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 2b69306d3c6e..ca9e4a284120 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -35,6 +35,8 @@ #include #include #include +#include +#include #define IS_FD_ARRAY(map) ((map)->map_type == BPF_MAP_TYPE_PERF_EVENT_ARRAY || \ (map)->map_type == BPF_MAP_TYPE_CGROUP_ARRAY || \ @@ -180,6 +182,13 @@ static int bpf_map_update_value(struct bpf_map *map, struct fd f, void *key, { int err; + if (map->map_flags & BPF_F_VERIFY_ELEM) { + err = bpf_map_verify_value_sig(value, bpf_map_value_size(map), + true); + if (err < 0) + return err; + } + /* Need to create a kthread, thus must support schedule */ if (bpf_map_is_dev_bound(map)) { return bpf_map_offload_update_elem(map, key, value, flags); @@ -1057,6 +1066,11 @@ static int map_create(union bpf_attr *attr) if (err) return -EINVAL; + /* Allow signed data to go through update/push methods only. */ + if ((attr->map_flags & BPF_F_VERIFY_ELEM) && + (attr->map_flags & BPF_F_MMAPABLE)) + return -EINVAL; + if (attr->btf_vmlinux_value_type_id) { if (attr->map_type != BPF_MAP_TYPE_STRUCT_OPS || attr->btf_key_type_id || attr->btf_value_type_id) @@ -1353,6 +1367,62 @@ static int map_lookup_elem(union bpf_attr *attr) return err; } +int bpf_map_verify_value_sig(const void *mod, size_t modlen, bool verify) +{ + const size_t marker_len = strlen(MODULE_SIG_STRING); + struct module_signature ms; + size_t sig_len; + u32 _modlen; + int ret; + + /* + * Format of mod: + * + * verified data+sig size (be32), verified data, sig, unverified data + */ + if (modlen <= sizeof(u32)) + return -ENOENT; + + _modlen = be32_to_cpu(*(u32 *)(mod)); + + if (_modlen > modlen - sizeof(u32)) + return -EINVAL; + + modlen = _modlen; + mod += sizeof(u32); + + if (modlen <= marker_len) + return -ENOENT; + + if (memcmp(mod + modlen - marker_len, MODULE_SIG_STRING, marker_len)) + return -ENOENT; + + modlen -= marker_len; + + if (modlen <= sizeof(ms)) + return -EBADMSG; + + memcpy(&ms, mod + (modlen - sizeof(ms)), sizeof(ms)); + + ret = mod_check_sig(&ms, modlen, "bpf_map_value"); + if (ret) + return ret; + + sig_len = be32_to_cpu(ms.sig_len); + modlen -= sig_len + sizeof(ms); + + if (verify) { + ret = verify_pkcs7_signature(mod, modlen, mod + modlen, sig_len, + VERIFY_USE_SECONDARY_KEYRING, + VERIFYING_UNSPECIFIED_SIGNATURE, + NULL, NULL); + if (ret < 0) + return ret; + } + + return modlen; +} +EXPORT_SYMBOL_GPL(bpf_map_verify_value_sig); #define BPF_MAP_UPDATE_ELEM_LAST_FIELD flags diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index f4009dbdf62d..a8e7803d2593 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -1226,6 +1226,9 @@ enum { /* Create a map that is suitable to be an inner map with dynamic max entries */ BPF_F_INNER_MAP = (1U << 12), + +/* Verify map value (fmt: ver data+sig size(be32), ver data, sig, unver data) */ + BPF_F_VERIFY_ELEM = (1U << 13) }; /* Flags for BPF_PROG_QUERY. */ From patchwork Wed May 25 13:21:14 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Roberto Sassu X-Patchwork-Id: 12861153 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id A6D28C43217 for ; Wed, 25 May 2022 13:21:49 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S239179AbiEYNVo (ORCPT ); Wed, 25 May 2022 09:21:44 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:52062 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232303AbiEYNVm (ORCPT ); Wed, 25 May 2022 09:21:42 -0400 Received: from frasgout.his.huawei.com (frasgout.his.huawei.com [185.176.79.56]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 29B6327FFE; Wed, 25 May 2022 06:21:38 -0700 (PDT) Received: from fraeml714-chm.china.huawei.com (unknown [172.18.147.201]) by frasgout.his.huawei.com (SkyGuard) with ESMTP id 4L7Wnl1gpnz681Yv; Wed, 25 May 2022 21:17:31 +0800 (CST) Received: from roberto-ThinkStation-P620.huawei.com (10.204.63.22) by fraeml714-chm.china.huawei.com (10.206.15.33) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2375.24; Wed, 25 May 2022 15:21:36 +0200 From: Roberto Sassu To: , , , CC: , , , , Roberto Sassu Subject: [PATCH 2/3] bpf: Introduce bpf_map_verified_data_size() helper Date: Wed, 25 May 2022 15:21:14 +0200 Message-ID: <20220525132115.896698-3-roberto.sassu@huawei.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20220525132115.896698-1-roberto.sassu@huawei.com> References: <20220525132115.896698-1-roberto.sassu@huawei.com> MIME-Version: 1.0 X-Originating-IP: [10.204.63.22] X-ClientProxiedBy: lhreml753-chm.china.huawei.com (10.201.108.203) To fraeml714-chm.china.huawei.com (10.206.15.33) X-CFilter-Loop: Reflected Precedence: bulk List-ID: X-Mailing-List: linux-kselftest@vger.kernel.org Introduce the bpf_map_verified_data_size() helper to get the verified data size from a signed map value, as parsed by the kernel with bpf_map_verify_value_sig(). The same information might be provided by user space tools as well without any helper, for example by adding a second unsigned integer after the verified data+sig size field. However, this alternative seems to increase the code complexity: the kernel has to parse two unsigned integers and check their consistency; user space tools have to parse the module-style appended signature to get the verified data size. Alternatively, each eBPF program could parse the module-style signature by itself, but this would cause duplication of the code. Adding a new helper seems the best choice, it only needs to call the existing function bpf_map_verify_value_sig() and pass the result to the caller. Signed-off-by: Roberto Sassu --- include/uapi/linux/bpf.h | 8 ++++++++ kernel/bpf/helpers.c | 15 +++++++++++++++ tools/include/uapi/linux/bpf.h | 8 ++++++++ 3 files changed, 31 insertions(+) diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index a8e7803d2593..4a05caa49419 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -5252,6 +5252,13 @@ union bpf_attr { * Pointer to the underlying dynptr data, NULL if the dynptr is * read-only, if the dynptr is invalid, or if the offset and length * is out of bounds. + * + * long bpf_map_verified_data_size(const void *value, u32 value_size) + * Description + * Parse signed map value in *value* with size *value_size*. + * Return + * The size of verified data on success, or a negative error in + * case of failure. */ #define __BPF_FUNC_MAPPER(FN) \ FN(unspec), \ @@ -5458,6 +5465,7 @@ union bpf_attr { FN(dynptr_read), \ FN(dynptr_write), \ FN(dynptr_data), \ + FN(bpf_map_verified_data_size), \ /* */ /* integer value in 'imm' field of BPF_CALL instruction selects which helper diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c index 225806a02efb..78c29c4e5d3f 100644 --- a/kernel/bpf/helpers.c +++ b/kernel/bpf/helpers.c @@ -1575,6 +1575,19 @@ const struct bpf_func_proto bpf_dynptr_data_proto = { .arg3_type = ARG_CONST_ALLOC_SIZE_OR_ZERO, }; +BPF_CALL_2(bpf_map_verified_data_size, const void *, value, u32, value_size) +{ + return bpf_map_verify_value_sig(value, value_size, false); +} + +const struct bpf_func_proto bpf_map_verified_data_size_proto = { + .func = bpf_map_verified_data_size, + .gpl_only = false, + .ret_type = RET_INTEGER, + .arg1_type = ARG_PTR_TO_MEM, + .arg2_type = ARG_CONST_SIZE_OR_ZERO, +}; + const struct bpf_func_proto bpf_get_current_task_proto __weak; const struct bpf_func_proto bpf_get_current_task_btf_proto __weak; const struct bpf_func_proto bpf_probe_read_user_proto __weak; @@ -1643,6 +1656,8 @@ bpf_base_func_proto(enum bpf_func_id func_id) return &bpf_dynptr_write_proto; case BPF_FUNC_dynptr_data: return &bpf_dynptr_data_proto; + case BPF_FUNC_bpf_map_verified_data_size: + return &bpf_map_verified_data_size_proto; default: break; } diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index a8e7803d2593..4a05caa49419 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -5252,6 +5252,13 @@ union bpf_attr { * Pointer to the underlying dynptr data, NULL if the dynptr is * read-only, if the dynptr is invalid, or if the offset and length * is out of bounds. + * + * long bpf_map_verified_data_size(const void *value, u32 value_size) + * Description + * Parse signed map value in *value* with size *value_size*. + * Return + * The size of verified data on success, or a negative error in + * case of failure. */ #define __BPF_FUNC_MAPPER(FN) \ FN(unspec), \ @@ -5458,6 +5465,7 @@ union bpf_attr { FN(dynptr_read), \ FN(dynptr_write), \ FN(dynptr_data), \ + FN(bpf_map_verified_data_size), \ /* */ /* integer value in 'imm' field of BPF_CALL instruction selects which helper From patchwork Wed May 25 13:21:15 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Roberto Sassu X-Patchwork-Id: 12861152 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 59FBAC433EF for ; Wed, 25 May 2022 13:21:49 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S241996AbiEYNVp (ORCPT ); Wed, 25 May 2022 09:21:45 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:52064 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230108AbiEYNVm (ORCPT ); Wed, 25 May 2022 09:21:42 -0400 Received: from frasgout.his.huawei.com (frasgout.his.huawei.com [185.176.79.56]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 998B4286DA; Wed, 25 May 2022 06:21:39 -0700 (PDT) Received: from fraeml714-chm.china.huawei.com (unknown [172.18.147.201]) by frasgout.his.huawei.com (SkyGuard) with ESMTP id 4L7Wpn0nPGz67PnL; Wed, 25 May 2022 21:18:25 +0800 (CST) Received: from roberto-ThinkStation-P620.huawei.com (10.204.63.22) by fraeml714-chm.china.huawei.com (10.206.15.33) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2375.24; Wed, 25 May 2022 15:21:37 +0200 From: Roberto Sassu To: , , , CC: , , , , Roberto Sassu Subject: [PATCH 3/3] bpf: Add tests for signed map values Date: Wed, 25 May 2022 15:21:15 +0200 Message-ID: <20220525132115.896698-4-roberto.sassu@huawei.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20220525132115.896698-1-roberto.sassu@huawei.com> References: <20220525132115.896698-1-roberto.sassu@huawei.com> MIME-Version: 1.0 X-Originating-IP: [10.204.63.22] X-ClientProxiedBy: lhreml753-chm.china.huawei.com (10.201.108.203) To fraeml714-chm.china.huawei.com (10.206.15.33) X-CFilter-Loop: Reflected Precedence: bulk List-ID: X-Mailing-List: linux-kselftest@vger.kernel.org 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 --- .../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 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#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 + */ + +#include "vmlinux.h" +#include +#include +#include + +#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; +}