From patchwork Mon Jan 23 14:51:44 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eduard Zingerman X-Patchwork-Id: 13112314 X-Patchwork-Delegate: bpf@iogearbox.net 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 30139C05027 for ; Mon, 23 Jan 2023 14:52:16 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231983AbjAWOwP (ORCPT ); Mon, 23 Jan 2023 09:52:15 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:55748 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231981AbjAWOwO (ORCPT ); Mon, 23 Jan 2023 09:52:14 -0500 Received: from mail-wm1-x32c.google.com (mail-wm1-x32c.google.com [IPv6:2a00:1450:4864:20::32c]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id A6C471DB8F for ; Mon, 23 Jan 2023 06:52:12 -0800 (PST) Received: by mail-wm1-x32c.google.com with SMTP id e19-20020a05600c439300b003db1cac0c1fso9297338wmn.5 for ; Mon, 23 Jan 2023 06:52:12 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=VZtwewBw0jZc1V/EXdclfT0bgMz05ZvL7GcUmamn1B8=; b=paXOBuUfqUX+uJLiw30B6kLCUPM4yxm9yuqI8+i4WoC1E6QaR5eyvPFInuLYveT4cm WXmDvYmzuRZ13R3XU7VpZTbB+Wun2CIS0hKCLepIhE3e0WPwNDMa2GPOts8pRI54GRdB vIBp470yrkq+JK3etgUlYYAw6IKsVdfSRG5q9jttLqpEYFqtLaELX3NuFETeQivlJZA0 OxRT5pAGL7tZjNyY4ZqyOkRkzMAiZUL5FzMmWR81SxKOsD1jai37mYP2UhBQSqX7W6Dh 3vYRIUhn6gv+S0H9y6t99pqP/Hyg8rCBQVXc1QrQ69SOoSulgYNLMG6ox0R0AViBjPkg qVXg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=VZtwewBw0jZc1V/EXdclfT0bgMz05ZvL7GcUmamn1B8=; b=HmqGyqnjse1M/jmn62DI8FtlxS4o1AFYFXbzqB4HqtgMzes19EfLg7aedR+1DsglAD cb5vkzba/r+Kb/q30Uh9TcdchqrxcOTOJWP6HPgh7ZWphtv9xVLMq+yocqz1+EDIIzZF HLNO9O/Bv8u7qYGv24CFxmNNpGgP4+M63VHE/+fuabB8T4/4FTVb7UQKTK1NKfWdj1qD aOCn3pvKvz106VjVxZHcGC9kEEZcnPYCNAOkbB6NvByGe7HsLKOCzR7BenZKzAyv/prT Gv1EpUjlpCxP8zk9zOZoDpAzldgZ62I0gc9fJCcdz0WicH1tgkP9lNnjMZdrk9Rtlueu 8Rsw== X-Gm-Message-State: AFqh2kq4VxA43MVqLETu2/vUS91iZPXyNKSLKbGxixyqT8IKVtICooxt K8fk/sikNqmhIRKqrckY5gKBJ9qq8M0= X-Google-Smtp-Source: AMrXdXusBEDuNpkjzH4RnXfbGI4FuR4lnpdTqHnp+W4YaQq7EOjj2AoCbv/tqbNyX+T+lpcS/V+BXw== X-Received: by 2002:a05:600c:34ce:b0:3db:25f:be9e with SMTP id d14-20020a05600c34ce00b003db025fbe9emr24388099wmq.33.1674485531027; Mon, 23 Jan 2023 06:52:11 -0800 (PST) Received: from pluto.. (host-176-36-0-241.b024.la.net.ua. [176.36.0.241]) by smtp.gmail.com with ESMTPSA id c7-20020a05600c0a4700b003d1e1f421bfsm11999649wmq.10.2023.01.23.06.52.10 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 23 Jan 2023 06:52:10 -0800 (PST) From: Eduard Zingerman To: bpf@vger.kernel.org, ast@kernel.org Cc: andrii@kernel.org, daniel@iogearbox.net, kernel-team@fb.com, yhs@fb.com, Eduard Zingerman Subject: [RFC bpf-next 1/5] selftests/bpf: support custom per-test flags and multiple expected messages Date: Mon, 23 Jan 2023 16:51:44 +0200 Message-Id: <20230123145148.2791939-2-eddyz87@gmail.com> X-Mailer: git-send-email 2.39.0 In-Reply-To: <20230123145148.2791939-1-eddyz87@gmail.com> References: <20230123145148.2791939-1-eddyz87@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: bpf@vger.kernel.org X-Patchwork-Delegate: bpf@iogearbox.net X-Patchwork-State: RFC From: Andrii Nakryiko Extend __flag attribute by allowing to specify one of the following: * BPF_F_STRICT_ALIGNMENT * BPF_F_ANY_ALIGNMENT * BPF_F_TEST_RND_HI32 * BPF_F_TEST_STATE_FREQ * BPF_F_SLEEPABLE * BPF_F_XDP_HAS_FRAGS * Some numeric value Extend __msg attribute by allowing to specify multiple exepcted messages. All messages are expected to be present in the verifier log in the order of application. Signed-off-by: Andrii Nakryiko [ Eduard: added commit message, formatting ] Signed-off-by: Eduard Zingerman --- tools/testing/selftests/bpf/test_loader.c | 69 ++++++++++++++++++++--- tools/testing/selftests/bpf/test_progs.h | 1 + 2 files changed, 61 insertions(+), 9 deletions(-) diff --git a/tools/testing/selftests/bpf/test_loader.c b/tools/testing/selftests/bpf/test_loader.c index 679efb3aa785..bf41390157bf 100644 --- a/tools/testing/selftests/bpf/test_loader.c +++ b/tools/testing/selftests/bpf/test_loader.c @@ -13,12 +13,15 @@ #define TEST_TAG_EXPECT_SUCCESS "comment:test_expect_success" #define TEST_TAG_EXPECT_MSG_PFX "comment:test_expect_msg=" #define TEST_TAG_LOG_LEVEL_PFX "comment:test_log_level=" +#define TEST_TAG_PROG_FLAGS_PFX "comment:test_prog_flags=" struct test_spec { const char *name; bool expect_failure; - const char *expect_msg; + const char **expect_msgs; + size_t expect_msg_cnt; int log_level; + int prog_flags; }; static int tester_init(struct test_loader *tester) @@ -67,7 +70,8 @@ static int parse_test_spec(struct test_loader *tester, for (i = 1; i < btf__type_cnt(btf); i++) { const struct btf_type *t; - const char *s; + const char *s, *val; + char *e; t = btf__type_by_id(btf, i); if (!btf_is_decl_tag(t)) @@ -82,14 +86,48 @@ static int parse_test_spec(struct test_loader *tester, } else if (strcmp(s, TEST_TAG_EXPECT_SUCCESS) == 0) { spec->expect_failure = false; } else if (str_has_pfx(s, TEST_TAG_EXPECT_MSG_PFX)) { - spec->expect_msg = s + sizeof(TEST_TAG_EXPECT_MSG_PFX) - 1; + void *tmp; + const char **msg; + + tmp = realloc(spec->expect_msgs, + (1 + spec->expect_msg_cnt) * sizeof(void *)); + if (!tmp) { + ASSERT_FAIL("failed to realloc memory for messages\n"); + return -ENOMEM; + } + spec->expect_msgs = tmp; + msg = &spec->expect_msgs[spec->expect_msg_cnt++]; + *msg = s + sizeof(TEST_TAG_EXPECT_MSG_PFX) - 1; } else if (str_has_pfx(s, TEST_TAG_LOG_LEVEL_PFX)) { + val = s + sizeof(TEST_TAG_LOG_LEVEL_PFX) - 1; errno = 0; - spec->log_level = strtol(s + sizeof(TEST_TAG_LOG_LEVEL_PFX) - 1, NULL, 0); - if (errno) { + spec->log_level = strtol(val, &e, 0); + if (errno || e[0] != '\0') { ASSERT_FAIL("failed to parse test log level from '%s'", s); return -EINVAL; } + } else if (str_has_pfx(s, TEST_TAG_PROG_FLAGS_PFX)) { + val = s + sizeof(TEST_TAG_PROG_FLAGS_PFX) - 1; + if (strcmp(val, "BPF_F_STRICT_ALIGNMENT") == 0) { + spec->prog_flags |= BPF_F_STRICT_ALIGNMENT; + } else if (strcmp(val, "BPF_F_ANY_ALIGNMENT") == 0) { + spec->prog_flags |= BPF_F_ANY_ALIGNMENT; + } else if (strcmp(val, "BPF_F_TEST_RND_HI32") == 0) { + spec->prog_flags |= BPF_F_TEST_RND_HI32; + } else if (strcmp(val, "BPF_F_TEST_STATE_FREQ") == 0) { + spec->prog_flags |= BPF_F_TEST_STATE_FREQ; + } else if (strcmp(val, "BPF_F_SLEEPABLE") == 0) { + spec->prog_flags |= BPF_F_SLEEPABLE; + } else if (strcmp(val, "BPF_F_XDP_HAS_FRAGS") == 0) { + spec->prog_flags |= BPF_F_XDP_HAS_FRAGS; + } else /* assume numeric value */ { + errno = 0; + spec->prog_flags |= strtol(val, &e, 0); + if (errno || e[0] != '\0') { + ASSERT_FAIL("failed to parse test prog flags from '%s'", s); + return -EINVAL; + } + } } } @@ -101,7 +139,7 @@ static void prepare_case(struct test_loader *tester, struct bpf_object *obj, struct bpf_program *prog) { - int min_log_level = 0; + int min_log_level = 0, prog_flags; if (env.verbosity > VERBOSE_NONE) min_log_level = 1; @@ -119,7 +157,11 @@ static void prepare_case(struct test_loader *tester, else bpf_program__set_log_level(prog, spec->log_level); + prog_flags = bpf_program__flags(prog); + bpf_program__set_flags(prog, prog_flags | spec->prog_flags); + tester->log_buf[0] = '\0'; + tester->next_match_pos = 0; } static void emit_verifier_log(const char *log_buf, bool force) @@ -135,17 +177,26 @@ static void validate_case(struct test_loader *tester, struct bpf_program *prog, int load_err) { - if (spec->expect_msg) { + int i, j; + + for (i = 0; i < spec->expect_msg_cnt; i++) { char *match; + const char *expect_msg; + + expect_msg = spec->expect_msgs[i]; - match = strstr(tester->log_buf, spec->expect_msg); + match = strstr(tester->log_buf + tester->next_match_pos, expect_msg); if (!ASSERT_OK_PTR(match, "expect_msg")) { /* if we are in verbose mode, we've already emitted log */ if (env.verbosity == VERBOSE_NONE) emit_verifier_log(tester->log_buf, true /*force*/); - fprintf(stderr, "EXPECTED MSG: '%s'\n", spec->expect_msg); + for (j = 0; j < i; j++) + fprintf(stderr, "MATCHED MSG: '%s'\n", spec->expect_msgs[j]); + fprintf(stderr, "EXPECTED MSG: '%s'\n", expect_msg); return; } + + tester->next_match_pos = match - tester->log_buf + strlen(expect_msg); } } diff --git a/tools/testing/selftests/bpf/test_progs.h b/tools/testing/selftests/bpf/test_progs.h index 3f058dfadbaf..9af80704f20a 100644 --- a/tools/testing/selftests/bpf/test_progs.h +++ b/tools/testing/selftests/bpf/test_progs.h @@ -410,6 +410,7 @@ int write_sysctl(const char *sysctl, const char *value); struct test_loader { char *log_buf; size_t log_buf_sz; + size_t next_match_pos; struct bpf_object *obj; }; From patchwork Mon Jan 23 14:51:45 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eduard Zingerman X-Patchwork-Id: 13112317 X-Patchwork-Delegate: bpf@iogearbox.net 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 1776DC38142 for ; Mon, 23 Jan 2023 14:52:20 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231775AbjAWOwS (ORCPT ); Mon, 23 Jan 2023 09:52:18 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:55800 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231993AbjAWOwR (ORCPT ); Mon, 23 Jan 2023 09:52:17 -0500 Received: from mail-wm1-x32f.google.com (mail-wm1-x32f.google.com [IPv6:2a00:1450:4864:20::32f]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 075E61D93F for ; Mon, 23 Jan 2023 06:52:14 -0800 (PST) Received: by mail-wm1-x32f.google.com with SMTP id l8so9216039wms.3 for ; Mon, 23 Jan 2023 06:52:13 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=jUK3JZVXp/bGX6GLm1XhNtLpF9tSREAfs4JtN+SxyP4=; b=CyoPE29l2nRIAD/WC1LGqnFDg5Dh2aNK2cDL7jh8xHhJOlYEXG1TL53XOYKhBxSlbG ekG7dGI7+7MG86U983HfPxNngtMGwlnnjc52zWSw0Zycn5WFYTD9hFlB27Db4q4vUKkT Exdt4tjyi108G3ipv6+iOi3Hv69zBbPGKiboseDfMk9EAbl2wo7RbmsB2wqJiu2yuhcR WNLdXBM7ouh8ci8FelO6tJaVMNC5NWHvljdOLDOR9J1xii3SKAdcbHfbFqDNPrBS1vhN lYaYoBmH6QR1MjvI3K+JznzRQ4rc4t7OgtgNXgqbCg7+Fxs68CX4v73MSBrG6W5PinBr jA9g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=jUK3JZVXp/bGX6GLm1XhNtLpF9tSREAfs4JtN+SxyP4=; b=qSld5iDADntBtc0QeVTU4Qi6uOwj1pzrEYKGMdwfEpYDLY59gyKdU8BaMNCUUxdMbT TyMYkiQ58sxXpCI43GAfekx0fCwq1GTywUp+Dk6s2x6GBaS0wlW9Ik7OK7XNZGbQ6yF7 f87SEwgxZacfNUSVmvx/VoBUEd3vRTGP5jm1AH0pSitI3Na2AMxkjdQQnXGSY0N3PlQR ZqBtgY+itigu0jK5jrawQ859WzhrnDpkMMX9a3ZLmBblZdye21xCek+QVg9Gd26FAEo9 PUQcjYBVIZwEM8SQ1R9epVRBRw+koiBQy4TgpsyxoffseuMEvGLZQEVrKLgSHXC6kuU4 aADQ== X-Gm-Message-State: AFqh2kpbLwMjW4iT4zZMoOHgjjpKl3ZffqxOtDVDvopajaTaXEi1UqwZ x7qimtGD6fAl38Bsq19ztUeYE9RTu6M= X-Google-Smtp-Source: AMrXdXvBZBlPS3XUM1wBK9MqkNaOwoBMe7oun3n/Dj8c2Fmmn6hz3sQ/NTV8oP2m8l/qY/TpX4rh2Q== X-Received: by 2002:a05:600c:1c8b:b0:3db:fe:f84e with SMTP id k11-20020a05600c1c8b00b003db00fef84emr23192896wms.1.1674485531953; Mon, 23 Jan 2023 06:52:11 -0800 (PST) Received: from pluto.. (host-176-36-0-241.b024.la.net.ua. [176.36.0.241]) by smtp.gmail.com with ESMTPSA id c7-20020a05600c0a4700b003d1e1f421bfsm11999649wmq.10.2023.01.23.06.52.11 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 23 Jan 2023 06:52:11 -0800 (PST) From: Eduard Zingerman To: bpf@vger.kernel.org, ast@kernel.org Cc: andrii@kernel.org, daniel@iogearbox.net, kernel-team@fb.com, yhs@fb.com, Eduard Zingerman Subject: [RFC bpf-next 2/5] selftests/bpf: unprivileged tests for test_loader.c Date: Mon, 23 Jan 2023 16:51:45 +0200 Message-Id: <20230123145148.2791939-3-eddyz87@gmail.com> X-Mailer: git-send-email 2.39.0 In-Reply-To: <20230123145148.2791939-1-eddyz87@gmail.com> References: <20230123145148.2791939-1-eddyz87@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: bpf@vger.kernel.org X-Patchwork-Delegate: bpf@iogearbox.net X-Patchwork-State: RFC Extends test_loader.c:test_loader__run_subtests() by allowing to execute tests in unprivileged mode, similar to test_verifier.c. Adds the following new attributes controlling test_loader behavior: __msg_unpriv __success_unpriv __failure_unpriv * If any of these attributes is present the test would be loaded in unprivileged mode. * If only "privileged" attributes are present the test would be loaded only in privileged mode. * If both "privileged" and "unprivileged" attributes are present the test would be loaded in both modes. * If test has to be executed in both modes, __msg(text) is specified and __msg_unpriv is not specified the behavior is the same as if __msg_unpriv(text) is specified. * For test filtering purposes the name of the program loaded in unprivileged mode is derived from the usual program name by adding `@unpriv' suffix. Also adds attribute '__description'. This attribute specifies text to be used instead of a program name for display and filtering purposes. Signed-off-by: Eduard Zingerman --- tools/testing/selftests/bpf/Makefile | 7 +- tools/testing/selftests/bpf/autoconf_helper.h | 9 + tools/testing/selftests/bpf/progs/bpf_misc.h | 48 +++ tools/testing/selftests/bpf/test_loader.c | 337 ++++++++++++++---- tools/testing/selftests/bpf/test_verifier.c | 25 +- tools/testing/selftests/bpf/unpriv_helpers.c | 26 ++ tools/testing/selftests/bpf/unpriv_helpers.h | 7 + 7 files changed, 359 insertions(+), 100 deletions(-) create mode 100644 tools/testing/selftests/bpf/autoconf_helper.h create mode 100644 tools/testing/selftests/bpf/unpriv_helpers.c create mode 100644 tools/testing/selftests/bpf/unpriv_helpers.h diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile index 22533a18705e..26e66f9a0977 100644 --- a/tools/testing/selftests/bpf/Makefile +++ b/tools/testing/selftests/bpf/Makefile @@ -218,8 +218,9 @@ TEST_GEN_PROGS_EXTENDED += $(DEFAULT_BPFTOOL) $(TEST_GEN_PROGS) $(TEST_GEN_PROGS_EXTENDED): $(BPFOBJ) -CGROUP_HELPERS := $(OUTPUT)/cgroup_helpers.o TESTING_HELPERS := $(OUTPUT)/testing_helpers.o +CGROUP_HELPERS := $(OUTPUT)/cgroup_helpers.o +UNPRIV_HELPERS := $(OUTPUT)/unpriv_helpers.o TRACE_HELPERS := $(OUTPUT)/trace_helpers.o CAP_HELPERS := $(OUTPUT)/cap_helpers.o @@ -238,7 +239,7 @@ $(OUTPUT)/test_lirc_mode2_user: $(TESTING_HELPERS) $(OUTPUT)/xdping: $(TESTING_HELPERS) $(OUTPUT)/flow_dissector_load: $(TESTING_HELPERS) $(OUTPUT)/test_maps: $(TESTING_HELPERS) -$(OUTPUT)/test_verifier: $(TESTING_HELPERS) $(CAP_HELPERS) +$(OUTPUT)/test_verifier: $(TESTING_HELPERS) $(CAP_HELPERS) $(UNPRIV_HELPERS) $(OUTPUT)/xsk.o: $(BPFOBJ) BPFTOOL ?= $(DEFAULT_BPFTOOL) @@ -527,7 +528,7 @@ TRUNNER_BPF_PROGS_DIR := progs TRUNNER_EXTRA_SOURCES := test_progs.c cgroup_helpers.c trace_helpers.c \ network_helpers.c testing_helpers.c \ btf_helpers.c flow_dissector_load.h \ - cap_helpers.c test_loader.c + cap_helpers.c test_loader.c unpriv_helpers.c TRUNNER_EXTRA_FILES := $(OUTPUT)/urandom_read $(OUTPUT)/bpf_testmod.ko \ $(OUTPUT)/liburandom_read.so \ $(OUTPUT)/xdp_synproxy \ diff --git a/tools/testing/selftests/bpf/autoconf_helper.h b/tools/testing/selftests/bpf/autoconf_helper.h new file mode 100644 index 000000000000..5b243b9cdf8c --- /dev/null +++ b/tools/testing/selftests/bpf/autoconf_helper.h @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#ifdef HAVE_GENHDR +# include "autoconf.h" +#else +# if defined(__i386) || defined(__x86_64) || defined(__s390x__) || defined(__aarch64__) +# define CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS 1 +# endif +#endif diff --git a/tools/testing/selftests/bpf/progs/bpf_misc.h b/tools/testing/selftests/bpf/progs/bpf_misc.h index 2d7b89b447b2..e742a935de98 100644 --- a/tools/testing/selftests/bpf/progs/bpf_misc.h +++ b/tools/testing/selftests/bpf/progs/bpf_misc.h @@ -2,10 +2,58 @@ #ifndef __BPF_MISC_H__ #define __BPF_MISC_H__ +/* This set of attributes controls behavior of the + * test_loader.c:test_loader__run_subtests(). + * + * The test_loader sequentially loads each program in a skeleton. + * Programs could be loaded in privileged and unprivileged modes. + * - __success, __failure, __msg imply privileged mode; + * - __success_unpriv, __failure_unpriv, __msg_unpriv imply + * unprivileged mode. + * If combination of privileged and unprivileged attributes is present + * both modes are used. If none are present privileged mode is implied. + * + * See test_loader.c:set_admin() for exact set of capabilities that + * differ between privileged and unprivileged modes. + * + * For test filtering purposes the name of the program loaded in + * unprivileged mode is derived from the usual program name by adding + * `@unpriv' suffix. + * + * __msg Message expected to be found in the verifier log. + * Multiple __msg attributes could be specified. + * __msg_unpriv Same as __msg but for unprivileged mode. + * + * __success Expect program load success in privileged mode. + * __success_unpriv Expect program load success in unprivileged mode. + * + * __failure Expect program load failure in privileged mode. + * __failure_unpriv Expect program load failure in unprivileged mode. + * + * __description Text to be used instead of a program name for display + * and filtering purposes. + * + * __log_level Log level to use for the program, numeric value expected. + * + * __flag Adds one flag use for the program, the following values are valid: + * - BPF_F_STRICT_ALIGNMENT; + * - BPF_F_TEST_RND_HI32; + * - BPF_F_TEST_STATE_FREQ; + * - BPF_F_SLEEPABLE; + * - BPF_F_XDP_HAS_FRAGS; + * - A numeric value. + * Multiple __flag attributes could be specified, the final flags + * value is derived by applying binary "or" to all specified values. + */ #define __msg(msg) __attribute__((btf_decl_tag("comment:test_expect_msg=" msg))) #define __failure __attribute__((btf_decl_tag("comment:test_expect_failure"))) #define __success __attribute__((btf_decl_tag("comment:test_expect_success"))) +#define __description(desc) __attribute__((btf_decl_tag("comment:test_description=" desc))) +#define __msg_unpriv(msg) __attribute__((btf_decl_tag("comment:test_expect_msg_unpriv=" msg))) +#define __failure_unpriv __attribute__((btf_decl_tag("comment:test_expect_failure_unpriv"))) +#define __success_unpriv __attribute__((btf_decl_tag("comment:test_expect_success_unpriv"))) #define __log_level(lvl) __attribute__((btf_decl_tag("comment:test_log_level="#lvl))) +#define __flag(flag) __attribute__((btf_decl_tag("comment:test_prog_flags="#flag))) /* Convenience macro for use with 'asm volatile' blocks */ #define __naked __attribute__((naked)) diff --git a/tools/testing/selftests/bpf/test_loader.c b/tools/testing/selftests/bpf/test_loader.c index bf41390157bf..f035f08c413c 100644 --- a/tools/testing/selftests/bpf/test_loader.c +++ b/tools/testing/selftests/bpf/test_loader.c @@ -1,9 +1,14 @@ // SPDX-License-Identifier: GPL-2.0-only /* Copyright (c) 2022 Meta Platforms, Inc. and affiliates. */ +#include #include #include #include +#include "autoconf_helper.h" +#include "unpriv_helpers.h" +#include "cap_helpers.h" + #define str_has_pfx(str, pfx) \ (strncmp(str, pfx, __builtin_constant_p(pfx) ? sizeof(pfx) - 1 : strlen(pfx)) == 0) @@ -12,16 +17,38 @@ #define TEST_TAG_EXPECT_FAILURE "comment:test_expect_failure" #define TEST_TAG_EXPECT_SUCCESS "comment:test_expect_success" #define TEST_TAG_EXPECT_MSG_PFX "comment:test_expect_msg=" +#define TEST_TAG_EXPECT_FAILURE_UNPRIV "comment:test_expect_failure_unpriv" +#define TEST_TAG_EXPECT_SUCCESS_UNPRIV "comment:test_expect_success_unpriv" +#define TEST_TAG_EXPECT_MSG_PFX_UNPRIV "comment:test_expect_msg_unpriv=" #define TEST_TAG_LOG_LEVEL_PFX "comment:test_log_level=" #define TEST_TAG_PROG_FLAGS_PFX "comment:test_prog_flags=" +#define TEST_TAG_DESCRIPTION_PFX "comment:test_description=" -struct test_spec { - const char *name; +#define ADMIN_CAPS (1ULL << CAP_SYS_ADMIN | \ + 1ULL << CAP_PERFMON | \ + 1ULL << CAP_BPF) + +static int sysctl_unpriv_disabled = -1; + +enum mode { + PRIV = 1, + UNPRIV = 2 +}; + +struct test_subspec { + char *name; bool expect_failure; const char **expect_msgs; size_t expect_msg_cnt; +}; + +struct test_spec { + const char *prog_name; + struct test_subspec priv; + struct test_subspec unpriv; int log_level; int prog_flags; + int mode_mask; }; static int tester_init(struct test_loader *tester) @@ -44,17 +71,46 @@ void test_loader_fini(struct test_loader *tester) free(tester->log_buf); } +static void free_test_spec(struct test_spec *spec) +{ + free(spec->priv.name); + free(spec->unpriv.name); + free(spec->priv.expect_msgs); + free(spec->unpriv.expect_msgs); +} + +static int push_msg(const char *msg, struct test_subspec *subspec) +{ + void *tmp; + + tmp = realloc(subspec->expect_msgs, (1 + subspec->expect_msg_cnt) * sizeof(void *)); + if (!tmp) { + ASSERT_FAIL("failed to realloc memory for messages\n"); + return -ENOMEM; + } + subspec->expect_msgs = tmp; + subspec->expect_msgs[subspec->expect_msg_cnt++] = msg; + + return 0; +} + +/* Uses btf_decl_tag attributes to describe the expected test + * behavior, see bpf_misc.h for detailed description of each attribute + * and attribute combinations. + */ static int parse_test_spec(struct test_loader *tester, struct bpf_object *obj, struct bpf_program *prog, struct test_spec *spec) { + const char *description = NULL; + bool has_unpriv_result = false; + int func_id, i, err = 0; struct btf *btf; - int func_id, i; memset(spec, 0, sizeof(*spec)); - spec->name = bpf_program__name(prog); + spec->prog_name = bpf_program__name(prog); btf = bpf_object__btf(obj); if (!btf) { @@ -62,15 +118,15 @@ static int parse_test_spec(struct test_loader *tester, return -EINVAL; } - func_id = btf__find_by_name_kind(btf, spec->name, BTF_KIND_FUNC); + func_id = btf__find_by_name_kind(btf, spec->prog_name, BTF_KIND_FUNC); if (func_id < 0) { - ASSERT_FAIL("failed to find FUNC BTF type for '%s'", spec->name); + ASSERT_FAIL("failed to find FUNC BTF type for '%s'", spec->prog_name); return -EINVAL; } for (i = 1; i < btf__type_cnt(btf); i++) { + const char *s, *val, *msg; const struct btf_type *t; - const char *s, *val; char *e; t = btf__type_by_id(btf, i); @@ -81,30 +137,42 @@ static int parse_test_spec(struct test_loader *tester, continue; s = btf__str_by_offset(btf, t->name_off); - if (strcmp(s, TEST_TAG_EXPECT_FAILURE) == 0) { - spec->expect_failure = true; + if (str_has_pfx(s, TEST_TAG_DESCRIPTION_PFX)) { + description = s + sizeof(TEST_TAG_DESCRIPTION_PFX) - 1; + } else if (strcmp(s, TEST_TAG_EXPECT_FAILURE) == 0) { + spec->priv.expect_failure = true; + spec->mode_mask |= PRIV; } else if (strcmp(s, TEST_TAG_EXPECT_SUCCESS) == 0) { - spec->expect_failure = false; + spec->priv.expect_failure = false; + spec->mode_mask |= PRIV; + } else if (strcmp(s, TEST_TAG_EXPECT_FAILURE_UNPRIV) == 0) { + spec->unpriv.expect_failure = true; + spec->mode_mask |= UNPRIV; + has_unpriv_result = true; + } else if (strcmp(s, TEST_TAG_EXPECT_SUCCESS_UNPRIV) == 0) { + spec->unpriv.expect_failure = false; + spec->mode_mask |= UNPRIV; + has_unpriv_result = true; } else if (str_has_pfx(s, TEST_TAG_EXPECT_MSG_PFX)) { - void *tmp; - const char **msg; - - tmp = realloc(spec->expect_msgs, - (1 + spec->expect_msg_cnt) * sizeof(void *)); - if (!tmp) { - ASSERT_FAIL("failed to realloc memory for messages\n"); - return -ENOMEM; - } - spec->expect_msgs = tmp; - msg = &spec->expect_msgs[spec->expect_msg_cnt++]; - *msg = s + sizeof(TEST_TAG_EXPECT_MSG_PFX) - 1; + msg = s + sizeof(TEST_TAG_EXPECT_MSG_PFX) - 1; + err = push_msg(msg, &spec->priv); + if (err) + goto cleanup; + spec->mode_mask |= PRIV; + } else if (str_has_pfx(s, TEST_TAG_EXPECT_MSG_PFX_UNPRIV)) { + msg = s + sizeof(TEST_TAG_EXPECT_MSG_PFX_UNPRIV) - 1; + err = push_msg(msg, &spec->unpriv); + if (err) + goto cleanup; + spec->mode_mask |= UNPRIV; } else if (str_has_pfx(s, TEST_TAG_LOG_LEVEL_PFX)) { val = s + sizeof(TEST_TAG_LOG_LEVEL_PFX) - 1; errno = 0; spec->log_level = strtol(val, &e, 0); if (errno || e[0] != '\0') { - ASSERT_FAIL("failed to parse test log level from '%s'", s); - return -EINVAL; + PRINT_FAIL("failed to parse test log level from '%s'\n", s); + err = -EINVAL; + goto cleanup; } } else if (str_has_pfx(s, TEST_TAG_PROG_FLAGS_PFX)) { val = s + sizeof(TEST_TAG_PROG_FLAGS_PFX) - 1; @@ -124,14 +192,70 @@ static int parse_test_spec(struct test_loader *tester, errno = 0; spec->prog_flags |= strtol(val, &e, 0); if (errno || e[0] != '\0') { - ASSERT_FAIL("failed to parse test prog flags from '%s'", s); - return -EINVAL; + PRINT_FAIL("failed to parse test prog flags from '%s'\n", + val); + err = -EINVAL; + goto cleanup; } } } } + if (spec->mode_mask == 0) + spec->mode_mask = PRIV; + + if (!description) + description = spec->prog_name; + + if (spec->mode_mask & PRIV) { + spec->priv.name = strdup(description); + if (!spec->priv.name) { + PRINT_FAIL("failed to allocate memory for priv.name\n"); + err = -ENOMEM; + goto cleanup; + } + } + + if (spec->mode_mask & UNPRIV) { + int descr_len = strlen(description); + const char *suffix = " @unpriv"; + char *name; + + name = malloc(descr_len + strlen(suffix) + 1); + if (!name) { + PRINT_FAIL("failed to allocate memory for unpriv.name\n"); + err = -ENOMEM; + goto cleanup; + } + + strcpy(name, description); + strcpy(&name[descr_len], suffix); + spec->unpriv.name = name; + } + + if (spec->mode_mask & (PRIV | UNPRIV)) { + if (!has_unpriv_result) + spec->unpriv.expect_failure = spec->priv.expect_failure; + + if (!spec->unpriv.expect_msgs) { + size_t sz = spec->priv.expect_msg_cnt * sizeof(void *); + + spec->unpriv.expect_msgs = malloc(sz); + if (!spec->unpriv.expect_msgs) { + PRINT_FAIL("failed to allocate memory for unpriv.expect_msgs\n"); + err = -ENOMEM; + goto cleanup; + } + memcpy(spec->unpriv.expect_msgs, spec->priv.expect_msgs, sz); + spec->unpriv.expect_msg_cnt = spec->priv.expect_msg_cnt; + } + } + return 0; + +cleanup: + free_test_spec(spec); + return err; } static void prepare_case(struct test_loader *tester, @@ -148,7 +272,7 @@ static void prepare_case(struct test_loader *tester, bpf_program__set_log_buf(prog, tester->log_buf, tester->log_buf_sz); - /* Make sure we set at least minimal log level, unless test requirest + /* Make sure we set at least minimal log level, unless test requires * even higher level already. Make sure to preserve independent log * level 4 (verifier stats), though. */ @@ -172,18 +296,18 @@ static void emit_verifier_log(const char *log_buf, bool force) } static void validate_case(struct test_loader *tester, - struct test_spec *spec, + struct test_subspec *subspec, struct bpf_object *obj, struct bpf_program *prog, int load_err) { int i, j; - for (i = 0; i < spec->expect_msg_cnt; i++) { + for (i = 0; i < subspec->expect_msg_cnt; i++) { char *match; const char *expect_msg; - expect_msg = spec->expect_msgs[i]; + expect_msg = subspec->expect_msgs[i]; match = strstr(tester->log_buf + tester->next_match_pos, expect_msg); if (!ASSERT_OK_PTR(match, "expect_msg")) { @@ -191,7 +315,8 @@ static void validate_case(struct test_loader *tester, if (env.verbosity == VERBOSE_NONE) emit_verifier_log(tester->log_buf, true /*force*/); for (j = 0; j < i; j++) - fprintf(stderr, "MATCHED MSG: '%s'\n", spec->expect_msgs[j]); + fprintf(stderr, + "MATCHED MSG: '%s'\n", subspec->expect_msgs[j]); fprintf(stderr, "EXPECTED MSG: '%s'\n", expect_msg); return; } @@ -200,17 +325,114 @@ static void validate_case(struct test_loader *tester, } } +static int set_admin(bool admin) +{ + int err; + + if (admin) + err = cap_enable_effective(ADMIN_CAPS, NULL); + else + err = cap_disable_effective(ADMIN_CAPS, NULL); + + if (err) + PRINT_FAIL("%s admin privs failed: %i, %s\n", + admin ? "gain" : "drop", err, strerror(err)); + + return err; +} + +static bool can_execute_unpriv(struct test_loader *tester, struct test_spec *spec) +{ +#ifdef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS + bool efficient_unaligned_access = true; +#else + bool efficient_unaligned_access = false; +#endif + if (sysctl_unpriv_disabled < 0) + sysctl_unpriv_disabled = get_unpriv_disabled() ? 1 : 0; + if (sysctl_unpriv_disabled) + return false; + if ((spec->prog_flags & BPF_F_ANY_ALIGNMENT) && !efficient_unaligned_access) + return false; + return true; +} + /* this function is forced noinline and has short generic name to look better * in test_progs output (in case of a failure) */ static noinline void run_subtest(struct test_loader *tester, - const char *skel_name, - skel_elf_bytes_fn elf_bytes_factory) + struct bpf_object_open_opts *open_opts, + const void *obj_bytes, + size_t obj_byte_cnt, + struct test_spec *spec, + bool unpriv) +{ + struct test_subspec *subspec = unpriv ? &spec->unpriv : &spec->priv; + struct bpf_program *tprog; + struct bpf_object *tobj; + int err; + + if (!test__start_subtest(subspec->name)) + return; + + if (unpriv && !can_execute_unpriv(tester, spec)) { + test__skip(); + test__end_subtest(); + return; + } + + if (unpriv) + set_admin(false); + + tobj = bpf_object__open_mem(obj_bytes, obj_byte_cnt, open_opts); + if (!ASSERT_OK_PTR(tobj, "obj_open_mem")) /* shouldn't happen */ + goto subtest_cleanup; + + bpf_object__for_each_program(tprog, tobj) + bpf_program__set_autoload(tprog, false); + + bpf_object__for_each_program(tprog, tobj) { + /* only load specified program */ + if (strcmp(bpf_program__name(tprog), spec->prog_name) == 0) { + bpf_program__set_autoload(tprog, true); + break; + } + } + + prepare_case(tester, spec, tobj, tprog); + + err = bpf_object__load(tobj); + if (subspec->expect_failure) { + if (!ASSERT_ERR(err, "unexpected_load_success")) { + emit_verifier_log(tester->log_buf, false /*force*/); + goto tobj_cleanup; + } + } else { + if (!ASSERT_OK(err, "unexpected_load_failure")) { + emit_verifier_log(tester->log_buf, true /*force*/); + goto tobj_cleanup; + } + } + + emit_verifier_log(tester->log_buf, false /*force*/); + validate_case(tester, subspec, tobj, tprog, err); + +tobj_cleanup: + bpf_object__close(tobj); +subtest_cleanup: + test__end_subtest(); + if (unpriv) + set_admin(true); +} + +static void process_subtest(struct test_loader *tester, + const char *skel_name, + skel_elf_bytes_fn elf_bytes_factory) { LIBBPF_OPTS(bpf_object_open_opts, open_opts, .object_name = skel_name); - struct bpf_object *obj = NULL, *tobj; - struct bpf_program *prog, *tprog; + struct bpf_object *obj = NULL; + struct bpf_program *prog; const void *obj_bytes; size_t obj_byte_cnt; int err; @@ -224,52 +446,19 @@ void run_subtest(struct test_loader *tester, return; bpf_object__for_each_program(prog, obj) { - const char *prog_name = bpf_program__name(prog); struct test_spec spec; - if (!test__start_subtest(prog_name)) - continue; - /* if we can't derive test specification, go to the next test */ err = parse_test_spec(tester, obj, prog, &spec); if (!ASSERT_OK(err, "parse_test_spec")) continue; - tobj = bpf_object__open_mem(obj_bytes, obj_byte_cnt, &open_opts); - if (!ASSERT_OK_PTR(tobj, "obj_open_mem")) /* shouldn't happen */ - continue; + if (spec.mode_mask & PRIV) + run_subtest(tester, &open_opts, obj_bytes, obj_byte_cnt, &spec, false); + if (spec.mode_mask & UNPRIV) + run_subtest(tester, &open_opts, obj_bytes, obj_byte_cnt, &spec, true); - bpf_object__for_each_program(tprog, tobj) - bpf_program__set_autoload(tprog, false); - - bpf_object__for_each_program(tprog, tobj) { - /* only load specified program */ - if (strcmp(bpf_program__name(tprog), prog_name) == 0) { - bpf_program__set_autoload(tprog, true); - break; - } - } - - prepare_case(tester, &spec, tobj, tprog); - - err = bpf_object__load(tobj); - if (spec.expect_failure) { - if (!ASSERT_ERR(err, "unexpected_load_success")) { - emit_verifier_log(tester->log_buf, false /*force*/); - goto tobj_cleanup; - } - } else { - if (!ASSERT_OK(err, "unexpected_load_failure")) { - emit_verifier_log(tester->log_buf, true /*force*/); - goto tobj_cleanup; - } - } - - emit_verifier_log(tester->log_buf, false /*force*/); - validate_case(tester, &spec, tobj, tprog, err); - -tobj_cleanup: - bpf_object__close(tobj); + free_test_spec(&spec); } bpf_object__close(obj); @@ -280,5 +469,5 @@ void test_loader__run_subtests(struct test_loader *tester, skel_elf_bytes_fn elf_bytes_factory) { /* see comment in run_subtest() for why we do this function nesting */ - run_subtest(tester, skel_name, elf_bytes_factory); + process_subtest(tester, skel_name, elf_bytes_factory); } diff --git a/tools/testing/selftests/bpf/test_verifier.c b/tools/testing/selftests/bpf/test_verifier.c index 8c808551dfd7..6071dff9c8a1 100644 --- a/tools/testing/selftests/bpf/test_verifier.c +++ b/tools/testing/selftests/bpf/test_verifier.c @@ -33,13 +33,8 @@ #include #include -#ifdef HAVE_GENHDR -# include "autoconf.h" -#else -# if defined(__i386) || defined(__x86_64) || defined(__s390x__) || defined(__aarch64__) -# define CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS 1 -# endif -#endif +#include "autoconf_helper.h" +#include "unpriv_helpers.h" #include "cap_helpers.h" #include "bpf_rand.h" #include "bpf_util.h" @@ -1665,22 +1660,6 @@ static bool is_admin(void) return (caps & ADMIN_CAPS) == ADMIN_CAPS; } -static void get_unpriv_disabled() -{ - char buf[2]; - FILE *fd; - - fd = fopen("/proc/sys/"UNPRIV_SYSCTL, "r"); - if (!fd) { - perror("fopen /proc/sys/"UNPRIV_SYSCTL); - unpriv_disabled = true; - return; - } - if (fgets(buf, 2, fd) == buf && atoi(buf)) - unpriv_disabled = true; - fclose(fd); -} - static bool test_as_unpriv(struct bpf_test *test) { #ifndef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS diff --git a/tools/testing/selftests/bpf/unpriv_helpers.c b/tools/testing/selftests/bpf/unpriv_helpers.c new file mode 100644 index 000000000000..488371edb3e4 --- /dev/null +++ b/tools/testing/selftests/bpf/unpriv_helpers.c @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include + +#include "unpriv_helpers.h" + +bool get_unpriv_disabled(void) +{ + bool disabled; + char buf[2]; + FILE *fd; + + fd = fopen("/proc/sys/"UNPRIV_SYSCTL, "r"); + if (fd) { + disabled = (fgets(buf, 2, fd) == buf && atoi(buf)); + fclose(fd); + } else { + perror("fopen /proc/sys/"UNPRIV_SYSCTL); + disabled = true; + } + + return disabled; +} diff --git a/tools/testing/selftests/bpf/unpriv_helpers.h b/tools/testing/selftests/bpf/unpriv_helpers.h new file mode 100644 index 000000000000..151f67329665 --- /dev/null +++ b/tools/testing/selftests/bpf/unpriv_helpers.h @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include + +#define UNPRIV_SYSCTL "kernel/unprivileged_bpf_disabled" + +bool get_unpriv_disabled(void); From patchwork Mon Jan 23 14:51:46 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eduard Zingerman X-Patchwork-Id: 13112315 X-Patchwork-Delegate: bpf@iogearbox.net 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 2E1D6C54E94 for ; Mon, 23 Jan 2023 14:52:18 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231984AbjAWOwQ (ORCPT ); Mon, 23 Jan 2023 09:52:16 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:55772 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231775AbjAWOwP (ORCPT ); Mon, 23 Jan 2023 09:52:15 -0500 Received: from mail-wm1-x335.google.com (mail-wm1-x335.google.com [IPv6:2a00:1450:4864:20::335]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id B3BDE1DB8F for ; Mon, 23 Jan 2023 06:52:14 -0800 (PST) Received: by mail-wm1-x335.google.com with SMTP id q10-20020a1cf30a000000b003db0edfdb74so4469454wmq.1 for ; Mon, 23 Jan 2023 06:52:14 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=WNkqEIwHTvn3hwJGCd8FxWiXUHPri3PBAlA0TPJRr3s=; b=jy5zehtW7P1TstsTHbcLTR3gCzsDqMHSvmFx+gTIxRyQ3fH9fCagRLXy4PU+zyW+ae fU/fPqz6ZZETLp+ZG/Aq4An0MliI8TsULaMI2gUrs3ABNFaqsxfLEXj/dEwcpxV9uJ0j 9ZIRsQgY9HI2EKtuiVxMvMUCiZp1RspjFYbtAGVgYiwYNterybjkOfehGCuc0dNFcOpt pzQ9NI4qiWDx2C/c8V5FWD4TzRzsJ1HQZIGVSUnkCQJpbFScX8l2lF0uAjR6rnh+SXFn Lk3WnXdKDwIzO6+2g/xQOgRGRywbM2htpTTowwAVkUG97ESbvJPjJH7bCUxOANAUj+dh 3jEw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=WNkqEIwHTvn3hwJGCd8FxWiXUHPri3PBAlA0TPJRr3s=; b=nuv6ezUZTQk00sifOV1Jfd0mBoZ7NbSeHpn/v1ADgvRk8+GhjFij1iumq+J8NpH9Db X2S+4Nee9kJ6Rc6Pe4SBzl2zJ2MF8yaX90TKHokFT3zj/CP6YQttEKxN6OBIpXzqWbUk qOvmCk99HDhih88sIU3Knam5yqS2uZgXw+BBGshgFCTLCsZa+ztusHpBwtHIyZvyENik 7nJIMFDkIu84sxYrs/TybkSobFwC5c4Zx9hiGu4EbWb71Gq2T5Iu/CIwzz++qpZSzFkC 0nWKCv/MwjNjqynV9l7yNUavBThqON8/SWRBfi6HIUF/gBYd1C93Z2Dr9T5lliG3HTIm 4qAQ== X-Gm-Message-State: AFqh2kplvAmdIvzhYTS1ruGFyApvC37pkWFx14/k/JSbSPVCQaWRbYsI 69Eroq04Ga/mQFSI4YB3kx9aWqYAQhc= X-Google-Smtp-Source: AMrXdXu627OFmK84+AewYvjfzQB/hlP6n+VpaSypsJoycZugIn5NOerN+jX5oeb09WTYCiUOfMIIlQ== X-Received: by 2002:a05:600c:c0c:b0:3db:1caf:1044 with SMTP id fm12-20020a05600c0c0c00b003db1caf1044mr18169921wmb.13.1674485532975; Mon, 23 Jan 2023 06:52:12 -0800 (PST) Received: from pluto.. (host-176-36-0-241.b024.la.net.ua. [176.36.0.241]) by smtp.gmail.com with ESMTPSA id c7-20020a05600c0a4700b003d1e1f421bfsm11999649wmq.10.2023.01.23.06.52.12 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 23 Jan 2023 06:52:12 -0800 (PST) From: Eduard Zingerman To: bpf@vger.kernel.org, ast@kernel.org Cc: andrii@kernel.org, daniel@iogearbox.net, kernel-team@fb.com, yhs@fb.com, Eduard Zingerman Subject: [RFC bpf-next 3/5] selftests/bpf: generate boilerplate code for test_loader-based tests Date: Mon, 23 Jan 2023 16:51:46 +0200 Message-Id: <20230123145148.2791939-4-eddyz87@gmail.com> X-Mailer: git-send-email 2.39.0 In-Reply-To: <20230123145148.2791939-1-eddyz87@gmail.com> References: <20230123145148.2791939-1-eddyz87@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: bpf@vger.kernel.org X-Patchwork-Delegate: bpf@iogearbox.net X-Patchwork-State: RFC Automatically generate boilerplate code necessary to run tests that use test_loader.c. Adds a target 'prog_tests/test_loader_auto_wrappers.c' as part of rulesets for 'test_progs' and 'test_progs-no_alu32'. The content of this C file is generated by make and has the following structure: #include #include "some_test_1.skel.h" #include "some_test_2.skel.h" ... void test_some_test_1(void) { RUN_TESTS(some_test_1); } void test_some_test_2(void) { RUN_TESTS(some_test_2); } ... Here RUN_TESTS is a macro defined in test_progs.h, it expands to a code that uses test_loader.c:test_loader__run_subtests() function to load tests specified by appropriate skel.h. In order to get the list of tests included in 'test_loader_auto_wrappers.c' the generation script looks for 'progs/*.c' files that contain a special comment: /* Use test_loader marker */ Signed-off-by: Eduard Zingerman --- tools/testing/selftests/bpf/Makefile | 34 +++++++++++++++++++ .../selftests/bpf/prog_tests/.gitignore | 1 + 2 files changed, 35 insertions(+) diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile index 26e66f9a0977..66ba2941677c 100644 --- a/tools/testing/selftests/bpf/Makefile +++ b/tools/testing/selftests/bpf/Makefile @@ -398,6 +398,11 @@ TRUNNER_OUTPUT := $(OUTPUT)$(if $2,/)$2 TRUNNER_BINARY := $1$(if $2,-)$2 TRUNNER_TEST_OBJS := $$(patsubst %.c,$$(TRUNNER_OUTPUT)/%.test.o, \ $$(notdir $$(wildcard $(TRUNNER_TESTS_DIR)/*.c))) +ifneq ($(TRUNNER_TEST_LOADER_AUTO_WRAPPERS),) +TRUNNER_TEST_OBJS := $$(filter-out $$(TRUNNER_OUTPUT)/test_loader_auto_wrappers.test.o, \ + $$(TRUNNER_TEST_OBJS)) +TRUNNER_TEST_OBJS += $$(TRUNNER_OUTPUT)/test_loader_auto_wrappers.test.o +endif TRUNNER_EXTRA_OBJS := $$(patsubst %.c,$$(TRUNNER_OUTPUT)/%.o, \ $$(filter %.c,$(TRUNNER_EXTRA_SOURCES))) TRUNNER_EXTRA_HDRS := $$(filter %.h,$(TRUNNER_EXTRA_SOURCES)) @@ -482,6 +487,32 @@ $(TRUNNER_TESTS_HDR): $(TRUNNER_TESTS_DIR)/*.c ) > $$@) endif +ifneq ($(TRUNNER_TEST_LOADER_AUTO_WRAPPERS),) +ifeq ($($(TRUNNER_TESTS_DIR)-test-loader-auto-wrappers-c),) +$(TRUNNER_TESTS_DIR)-test-loader-auto-wrappers-c := y +$(TRUNNER_TESTS_DIR)/test_loader_auto_wrappers.c: $(TRUNNER_BPF_PROGS_DIR)/*.c + $$(call msg,GEN-TEST,$(TRUNNER_BINARY),$$@) + $$(shell (echo '/* Generated source, do not edit */'; \ + tests=$$$$(grep --null -lF '/* Use test_loader marker */' \ + $$(TRUNNER_BPF_PROGS_DIR)/*.c \ + | xargs -0 -I {} basename {} .c); \ + echo "#include "; \ + echo ""; \ + for case in $$$$tests; \ + do \ + echo "#include \"$$$$case.skel.h\""; \ + done; \ + echo ""; \ + for case in $$$$tests; \ + do \ + printf "void %-50s { %-50s }\n" \ + "test_$$$$case(void)" \ + "RUN_TESTS($$$$case);"; \ + done) > $$@) +$(TRUNNER_TESTS_HDR): $(TRUNNER_TESTS_DIR)/test_loader_auto_wrappers.c +endif +endif # TRUNNER_TEST_LOADER_AUTO_WRAPPERS + # compile individual test files # Note: we cd into output directory to ensure embedded BPF object is found $(TRUNNER_TEST_OBJS): $(TRUNNER_OUTPUT)/%.test.o: \ @@ -537,6 +568,7 @@ TRUNNER_EXTRA_FILES := $(OUTPUT)/urandom_read $(OUTPUT)/bpf_testmod.ko \ verify_sig_setup.sh \ $(wildcard progs/btf_dump_test_case_*.c) \ $(wildcard progs/*.bpf.o) +TRUNNER_TEST_LOADER_AUTO_WRAPPERS := t TRUNNER_BPF_BUILD_RULE := CLANG_BPF_BUILD_RULE TRUNNER_BPF_CFLAGS := $(BPF_CFLAGS) $(CLANG_CFLAGS) -DENABLE_ATOMICS_TESTS $(eval $(call DEFINE_TEST_RUNNER,test_progs)) @@ -560,6 +592,7 @@ TRUNNER_EXTRA_SOURCES := test_maps.c TRUNNER_EXTRA_FILES := TRUNNER_BPF_BUILD_RULE := $$(error no BPF objects should be built) TRUNNER_BPF_CFLAGS := +TRUNNER_TEST_LOADER_AUTO_WRAPPERS := $(eval $(call DEFINE_TEST_RUNNER,test_maps)) # Define test_verifier test runner. @@ -625,6 +658,7 @@ $(OUTPUT)/veristat: $(OUTPUT)/veristat.o EXTRA_CLEAN := $(TEST_CUSTOM_PROGS) $(SCRATCH_DIR) $(HOST_SCRATCH_DIR) \ prog_tests/tests.h map_tests/tests.h verifier/tests.h \ + prog_tests/test_loader_auto_wrappers.c \ feature bpftool \ $(addprefix $(OUTPUT)/,*.o *.skel.h *.lskel.h *.subskel.h \ no_alu32 bpf_gcc bpf_testmod.ko \ diff --git a/tools/testing/selftests/bpf/prog_tests/.gitignore b/tools/testing/selftests/bpf/prog_tests/.gitignore index 89c4a3d37544..6a937c1cd78d 100644 --- a/tools/testing/selftests/bpf/prog_tests/.gitignore +++ b/tools/testing/selftests/bpf/prog_tests/.gitignore @@ -1,2 +1,3 @@ # SPDX-License-Identifier: GPL-2.0-only tests.h +test_loader_auto_wrappers.c From patchwork Mon Jan 23 14:51:47 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eduard Zingerman X-Patchwork-Id: 13112316 X-Patchwork-Delegate: bpf@iogearbox.net 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 460FEC05027 for ; Mon, 23 Jan 2023 14:52:19 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231770AbjAWOwS (ORCPT ); Mon, 23 Jan 2023 09:52:18 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:55790 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231991AbjAWOwQ (ORCPT ); Mon, 23 Jan 2023 09:52:16 -0500 Received: from mail-wm1-x334.google.com (mail-wm1-x334.google.com [IPv6:2a00:1450:4864:20::334]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 804BC21A34 for ; Mon, 23 Jan 2023 06:52:15 -0800 (PST) Received: by mail-wm1-x334.google.com with SMTP id l8so9216090wms.3 for ; Mon, 23 Jan 2023 06:52:15 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=cNu9QsQPY2wu8htmYk3im2OcdGmOEVzjAIGzhmpVUyA=; b=PcaQ28kH4SooClUlQz95G9uZ8tcSGUme7UrShXExdvQZEu0+ZmqtlNONtrDsCSwBZ0 w09/7zg5q1XeCkarCvdnps54Txg7CVonQJhy/1FT01SgVrKsJPNEQC2SYSboPSCKE+ul ptWUOy9xNliPQggk6uIPKVWQV1csyBEwfvTMcTm1hIpnyT3Y53Rev5LzFumJg4c4UbFG rRCA4qPMRP2ltvDfjKH1kAaJvhH4R2ZNOoJkSFh2cEomCMq6u7boSWKKVBC+ZtDFqKES wubBYUo8rIcPmUdWVG6CAUGh9GWWn+Akz74f9LcSaKPfGYclAf/+1L9PNhft/r2lRs4s onvA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=cNu9QsQPY2wu8htmYk3im2OcdGmOEVzjAIGzhmpVUyA=; b=wC6DzCJsZUCjbn59t01T+G7/MXzROXi4q/j2J8Uah5uhTQozyhEx6nF5O6FLJhivs2 1haGHtYR763Rp6OBV83+c9LcgwwTVtcQS9YPu8SPD/hhKOmJ9e0bd3VkUQluYw/c7N9w Dfnn0N65xAF1Sn4o2H0e9CJ33ICR2FZbyN8M47e4+MPYA9O7rY0at9zfwcWaQqYs1wZt 6Tnfjmy3e9SgW7RPHNp+CYJRh0XiJvpVcx9ZGq7cYYFBWAhteFWo9RliLPGsVxhx/SQ+ 0Bj+4UgnSsg8YrGwqOCDTxP7MBrIYG6ZzlDCmjUTD5z74mEMUABgVdiTEfuNaSN07jbW xYlA== X-Gm-Message-State: AFqh2krCoBjsLfOZfmSKjZ3YDWzd7FDwwDNXNlNc8Nsv1dqGh1luBDHG hw+nV5KnSdv4BV16Sj2hap4P6eb4hMg= X-Google-Smtp-Source: AMrXdXtE/+Ze+aPJPbr15DWumFz/8NGqEg8OlFEgSb1A+VEGO4M2cOm3GWe8Ovw9igFz19tFe96neg== X-Received: by 2002:a7b:c5c4:0:b0:3d7:889:7496 with SMTP id n4-20020a7bc5c4000000b003d708897496mr23733156wmk.17.1674485533906; Mon, 23 Jan 2023 06:52:13 -0800 (PST) Received: from pluto.. (host-176-36-0-241.b024.la.net.ua. [176.36.0.241]) by smtp.gmail.com with ESMTPSA id c7-20020a05600c0a4700b003d1e1f421bfsm11999649wmq.10.2023.01.23.06.52.13 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 23 Jan 2023 06:52:13 -0800 (PST) From: Eduard Zingerman To: bpf@vger.kernel.org, ast@kernel.org Cc: andrii@kernel.org, daniel@iogearbox.net, kernel-team@fb.com, yhs@fb.com, Eduard Zingerman Subject: [RFC bpf-next 4/5] selftests/bpf: __imm_insn macro to embed raw insns in inline asm Date: Mon, 23 Jan 2023 16:51:47 +0200 Message-Id: <20230123145148.2791939-5-eddyz87@gmail.com> X-Mailer: git-send-email 2.39.0 In-Reply-To: <20230123145148.2791939-1-eddyz87@gmail.com> References: <20230123145148.2791939-1-eddyz87@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: bpf@vger.kernel.org X-Patchwork-Delegate: bpf@iogearbox.net X-Patchwork-State: RFC A convenience macro to allow the following usage: #include ... asm volatile ( ... ".8byte %[raw_insn];" ... : : __imm_insn(raw_insn, BPF_RAW_INSN(...)) : __clobber_all); Signed-off-by: Eduard Zingerman --- tools/testing/selftests/bpf/progs/bpf_misc.h | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/testing/selftests/bpf/progs/bpf_misc.h b/tools/testing/selftests/bpf/progs/bpf_misc.h index e742a935de98..832bec4818d9 100644 --- a/tools/testing/selftests/bpf/progs/bpf_misc.h +++ b/tools/testing/selftests/bpf/progs/bpf_misc.h @@ -61,6 +61,7 @@ #define __clobber_common "r0", "r1", "r2", "r3", "r4", "r5", "memory" #define __imm(name) [name]"i"(name) #define __imm_addr(name) [name]"i"(&name) +#define __imm_insn(name, expr) [name]"i"(*(long *)&(expr)) #if defined(__TARGET_ARCH_x86) #define SYSCALL_WRAPPER 1 From patchwork Mon Jan 23 14:51:48 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eduard Zingerman X-Patchwork-Id: 13112318 X-Patchwork-Delegate: bpf@iogearbox.net 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 E43D9C54E94 for ; Mon, 23 Jan 2023 14:52:21 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230245AbjAWOwU (ORCPT ); Mon, 23 Jan 2023 09:52:20 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:55832 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231985AbjAWOwS (ORCPT ); Mon, 23 Jan 2023 09:52:18 -0500 Received: from mail-wm1-x331.google.com (mail-wm1-x331.google.com [IPv6:2a00:1450:4864:20::331]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 9BB76227A6 for ; Mon, 23 Jan 2023 06:52:16 -0800 (PST) Received: by mail-wm1-x331.google.com with SMTP id m5-20020a05600c4f4500b003db03b2559eso8742656wmq.5 for ; Mon, 23 Jan 2023 06:52:16 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=KwZOPyOCDfCNfEwj1LZpRu0/ZN5cDHbQPABExyOPQnc=; b=QRrnEu0vnPDdSIYTlN0kW+fKjwBMezL+CKmY2lZdXnE9AkI/x9Zgsrkj9Ut9pLJ+jA u+NTvcu3VB4a0pzIK45itjELLbiO6RIccwFRo67guh1OWOA2WMBn/lUBp3eZYVn/RXN1 jl/ltBXnh54gXVmjwgsmgkmT1g80EdB2h4aDPUo8h2nWvTrFAsY3aj/JZCId7d4bllIg RqlMq6RUDcD6fuKTvDou1lbBS50evWefJYPAqbljpQnAgC2eoh73RZ9s/CSQh8DMao+R 30boXyLmcsVbThw3TJZthKJQzodGFnXu0p6F7VH0YPOo07XPqlP8oXrNOJX21SXtkULp axNg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=KwZOPyOCDfCNfEwj1LZpRu0/ZN5cDHbQPABExyOPQnc=; b=QjNKsNVooZ2tkAbeC0tXouAIdTV6FS/xdhO7Mqr6NGRCtNtfKZDIIu6WRhcKN2uBWo 67mONWImmMcUYbd6gXx3EJ2DMhnCwqlX96V1XysLLQQl8cl4TCagZz5lzst8ljR4uVrs V/X3XJ4UTwALLutavnJbuxYFCSsZbU7yVMBG9iQ0IX/OaHLi0fzBVQE04ai0RPmswoRs N/ATqq3fSApyir2j3SFhDjWNonTS7+NKFe4eOBciDcXvR1wgXbeMIMe3JnDMUCMU05V7 Q3LHt3xRuGRNRtMhIoWz3S7UMuU6YAkR5srgoxv1TXl9paUppV8EnqqKoJr87sroFReQ wdWA== X-Gm-Message-State: AFqh2krUm2ZCmuNrVq734VhHfaVrpJnVbHQLCZ6pYKlbIJkjoWBaTaC1 nU0M5sTZTSYGPRwgdWsDoiHYYxRZd0E= X-Google-Smtp-Source: AMrXdXtVa1D5dPXndSNObtT8aBBhhuAF2p7YrgcjIWxXlnmN66tmrDNnSpoDhxmZstWrX66G/NeAnQ== X-Received: by 2002:a05:600c:5390:b0:3d9:a145:4d1a with SMTP id hg16-20020a05600c539000b003d9a1454d1amr21105630wmb.34.1674485534807; Mon, 23 Jan 2023 06:52:14 -0800 (PST) Received: from pluto.. (host-176-36-0-241.b024.la.net.ua. [176.36.0.241]) by smtp.gmail.com with ESMTPSA id c7-20020a05600c0a4700b003d1e1f421bfsm11999649wmq.10.2023.01.23.06.52.13 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 23 Jan 2023 06:52:14 -0800 (PST) From: Eduard Zingerman To: bpf@vger.kernel.org, ast@kernel.org Cc: andrii@kernel.org, daniel@iogearbox.net, kernel-team@fb.com, yhs@fb.com, Eduard Zingerman Subject: [RFC bpf-next 5/5] selftests/bpf: convert jeq_infer_not_null tests to inline assembly Date: Mon, 23 Jan 2023 16:51:48 +0200 Message-Id: <20230123145148.2791939-6-eddyz87@gmail.com> X-Mailer: git-send-email 2.39.0 In-Reply-To: <20230123145148.2791939-1-eddyz87@gmail.com> References: <20230123145148.2791939-1-eddyz87@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: bpf@vger.kernel.org X-Patchwork-Delegate: bpf@iogearbox.net X-Patchwork-State: RFC Use updated test_loader interface to convert progs/jeq_infer_not_null verifier test to inline assembly. Some redundant comments are removed in the process. Existing test progs/jeq_infer_not_null_fail.c is updated to use "Use test_loader marker" to remove trivial progs_tests/jeq_infer_not_null.c boilerplate code. Signed-off-by: Eduard Zingerman --- .../bpf/prog_tests/jeq_infer_not_null.c | 9 - .../selftests/bpf/progs/jeq_infer_not_null.c | 186 ++++++++++++++++++ .../bpf/progs/jeq_infer_not_null_fail.c | 1 + .../bpf/verifier/jeq_infer_not_null.c | 174 ---------------- 4 files changed, 187 insertions(+), 183 deletions(-) delete mode 100644 tools/testing/selftests/bpf/prog_tests/jeq_infer_not_null.c create mode 100644 tools/testing/selftests/bpf/progs/jeq_infer_not_null.c delete mode 100644 tools/testing/selftests/bpf/verifier/jeq_infer_not_null.c diff --git a/tools/testing/selftests/bpf/prog_tests/jeq_infer_not_null.c b/tools/testing/selftests/bpf/prog_tests/jeq_infer_not_null.c deleted file mode 100644 index 3add34df5767..000000000000 --- a/tools/testing/selftests/bpf/prog_tests/jeq_infer_not_null.c +++ /dev/null @@ -1,9 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -#include -#include "jeq_infer_not_null_fail.skel.h" - -void test_jeq_infer_not_null(void) -{ - RUN_TESTS(jeq_infer_not_null_fail); -} diff --git a/tools/testing/selftests/bpf/progs/jeq_infer_not_null.c b/tools/testing/selftests/bpf/progs/jeq_infer_not_null.c new file mode 100644 index 000000000000..7c506eccacaf --- /dev/null +++ b/tools/testing/selftests/bpf/progs/jeq_infer_not_null.c @@ -0,0 +1,186 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Converted from tools/testing/selftests/bpf/verifier/jeq_infer_not_null.c */ +/* Use test_loader marker */ + +#include +#include +#include "bpf_misc.h" + +struct { + __uint(type, BPF_MAP_TYPE_XSKMAP); + __uint(max_entries, 1); + __type(key, int); + __type(value, int); +} map_xskmap SEC(".maps"); + +/* This is equivalent to the following program: + * + * r6 = skb->sk; + * r7 = sk_fullsock(r6); + * r0 = sk_fullsock(r6); + * if (r0 == 0) return 0; (a) + * if (r0 != r7) return 0; (b) + * *r7->type; (c) + * return 0; + * + * It is safe to dereference r7 at point (c), because of (a) and (b). + * The test verifies that relation r0 == r7 is propagated from (b) to (c). + */ +__description("jne/jeq infer not null, PTR_TO_SOCKET_OR_NULL -> PTR_TO_SOCKET for JNE false branch") +__success __failure_unpriv __msg_unpriv("R7 pointer comparison") +SEC("cgroup/skb") +__naked void sock_or_null_jne_false_branch(void) +{ + asm volatile ( +" r6 = *(u64*)(r1 + %[__sk_buff_sk_offset]); \n\ + if r6 == 0 goto exit_%=; \n\ + r1 = r6; \n\ + call %[bpf_sk_fullsock]; \n\ + r7 = r0; \n\ + r1 = r6; \n\ + call %[bpf_sk_fullsock]; \n\ + if r0 == 0 goto exit_%=; \n\ + if r0 != r7 goto exit_%=; \n\ + r0 = *(u32*)(r7 + %[bpf_sock_type_offset]); \n\ +exit_%=: \n\ + r0 = 0; \n\ + exit; \n\ +" : + : [__sk_buff_sk_offset]"i"(offsetof(struct __sk_buff, sk)), + [bpf_sock_type_offset]"i"(offsetof(struct bpf_sock, type)), + __imm(bpf_sk_fullsock) + : __clobber_all); +} + +/* Same as above, but verify that another branch of JNE still + * prohibits access to PTR_MAYBE_NULL. + */ +__description("jne/jeq infer not null, PTR_TO_SOCKET_OR_NULL unchanged for JNE true branch") +__failure __msg("R7 invalid mem access 'sock_or_null'") +__failure_unpriv __msg_unpriv("R7 pointer comparison") +SEC("cgroup/skb") +__naked void sock_or_null_jne_true_branch(void) +{ + asm volatile ( +" r6 = *(u64*)(r1 + %[__sk_buff_sk_offset]); \n\ + if r6 == 0 goto exit_%=; \n\ + r1 = r6; \n\ + call %[bpf_sk_fullsock]; \n\ + r7 = r0; \n\ + r1 = r6; \n\ + call %[bpf_sk_fullsock]; \n\ + if r0 != 0 goto exit_%=; \n\ + if r0 != r7 goto l1_%=; \n\ + goto exit_%=; \n\ +l1_%=: \n\ + r0 = *(u32*)(r7 + %[bpf_sock_type_offset]); \n\ +exit_%=: \n\ + r0 = 0; \n\ + exit; \n\ +" : + : [__sk_buff_sk_offset]"i"(offsetof(struct __sk_buff, sk)), + [bpf_sock_type_offset]"i"(offsetof(struct bpf_sock, type)), + __imm(bpf_sk_fullsock) + : __clobber_all); +} + +/* Same as a first test, but not null should be inferred for JEQ branch */ +__description("jne/jeq infer not null, PTR_TO_SOCKET_OR_NULL -> PTR_TO_SOCKET for JEQ true branch") +__success __failure_unpriv __msg_unpriv("R7 pointer comparison") +SEC("cgroup/skb") +__naked void sock_or_null_jeq_true_branch(void) +{ + asm volatile ( +" r6 = *(u64*)(r1 + %[__sk_buff_sk_offset]); \n\ + if r6 == 0 goto exit_%=; \n\ + r1 = r6; \n\ + call %[bpf_sk_fullsock]; \n\ + r7 = r0; \n\ + r1 = r6; \n\ + call %[bpf_sk_fullsock]; \n\ + if r0 == 0 goto exit_%=; \n\ + if r0 == r7 goto l1_%=; \n\ + goto exit_%=; \n\ +l1_%=: \n\ + r0 = *(u32*)(r7 + %[bpf_sock_type_offset]); \n\ +exit_%=: \n\ + r0 = 0; \n\ + exit; \n\ +" : + : [__sk_buff_sk_offset]"i"(offsetof(struct __sk_buff, sk)), + [bpf_sock_type_offset]"i"(offsetof(struct bpf_sock, type)), + __imm(bpf_sk_fullsock) + : __clobber_all); +} + +/* Same as above, but verify that another branch of JNE still + * prohibits access to PTR_MAYBE_NULL. + */ +__description("jne/jeq infer not null, PTR_TO_SOCKET_OR_NULL unchanged for JEQ false branch") +__failure __msg("R7 invalid mem access 'sock_or_null'") +__failure_unpriv __msg_unpriv("R7 pointer comparison") +SEC("cgroup/skb") +__naked void sock_or_null_jeq_false_branch(void) +{ + asm volatile ( +" r6 = *(u64*)(r1 + %[__sk_buff_sk_offset]); \n\ + if r6 == 0 goto exit_%=; \n\ + r1 = r6; \n\ + call %[bpf_sk_fullsock]; \n\ + r7 = r0; \n\ + r1 = r6; \n\ + call %[bpf_sk_fullsock]; \n\ + if r0 == 0 goto exit_%=; \n\ + if r0 == r7 goto exit_%=; \n\ + r0 = *(u32*)(r7 + %[bpf_sock_type_offset]); \n\ +exit_%=: \n\ + r0 = 0; \n\ + exit; \n\ +" : + : [__sk_buff_sk_offset]"i"(offsetof(struct __sk_buff, sk)), + [bpf_sock_type_offset]"i"(offsetof(struct bpf_sock, type)), + __imm(bpf_sk_fullsock) + : __clobber_all); +} + +/* Maps are treated in a different branch of `mark_ptr_not_null_reg`, + * so separate test for maps case. + */ +__description("jne/jeq infer not null, PTR_TO_MAP_VALUE_OR_NULL -> PTR_TO_MAP_VALUE") +__success +SEC("xdp") +__naked void ptr_to_map(void) +{ + asm volatile ( +" r1 = 0; \n\ + *(u32*)(r10 - 8) = r1; \n\ + r9 = r10; \n\ + r9 += -8; \n\ + /* r8 = process local map */ \n\ + r8 = %[map_xskmap] ll; \n\ + /* r6 = map_lookup_elem(r8, r9); */ \n\ + r1 = r8; \n\ + r2 = r9; \n\ + call %[bpf_map_lookup_elem]; \n\ + r6 = r0; \n\ + /* r7 = map_lookup_elem(r8, r9); */ \n\ + r1 = r8; \n\ + r2 = r9; \n\ + call %[bpf_map_lookup_elem]; \n\ + r7 = r0; \n\ + if r6 == 0 goto exit_%=; \n\ + if r6 != r7 goto exit_%=; \n\ + /* read *r7; */ \n\ + r0 = *(u32*)(r7 + %[bpf_xdp_sock_queue_id_offset]);\n\ +exit_%=: \n\ + r0 = 0; \n\ + exit; \n\ +" : + : [bpf_xdp_sock_queue_id_offset]"i"(offsetof(struct bpf_xdp_sock, queue_id)), + __imm(bpf_map_lookup_elem), + __imm_addr(map_xskmap) + : __clobber_all); +} + +char _license[] SEC("license") = "GPL"; + diff --git a/tools/testing/selftests/bpf/progs/jeq_infer_not_null_fail.c b/tools/testing/selftests/bpf/progs/jeq_infer_not_null_fail.c index f46965053acb..8048d76f60ca 100644 --- a/tools/testing/selftests/bpf/progs/jeq_infer_not_null_fail.c +++ b/tools/testing/selftests/bpf/progs/jeq_infer_not_null_fail.c @@ -1,4 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 +/* Use test_loader marker */ #include "vmlinux.h" #include diff --git a/tools/testing/selftests/bpf/verifier/jeq_infer_not_null.c b/tools/testing/selftests/bpf/verifier/jeq_infer_not_null.c deleted file mode 100644 index 67a1c07ead34..000000000000 --- a/tools/testing/selftests/bpf/verifier/jeq_infer_not_null.c +++ /dev/null @@ -1,174 +0,0 @@ -{ - /* This is equivalent to the following program: - * - * r6 = skb->sk; - * r7 = sk_fullsock(r6); - * r0 = sk_fullsock(r6); - * if (r0 == 0) return 0; (a) - * if (r0 != r7) return 0; (b) - * *r7->type; (c) - * return 0; - * - * It is safe to dereference r7 at point (c), because of (a) and (b). - * The test verifies that relation r0 == r7 is propagated from (b) to (c). - */ - "jne/jeq infer not null, PTR_TO_SOCKET_OR_NULL -> PTR_TO_SOCKET for JNE false branch", - .insns = { - /* r6 = skb->sk; */ - BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, offsetof(struct __sk_buff, sk)), - /* if (r6 == 0) return 0; */ - BPF_JMP_IMM(BPF_JEQ, BPF_REG_6, 0, 8), - /* r7 = sk_fullsock(skb); */ - BPF_MOV64_REG(BPF_REG_1, BPF_REG_6), - BPF_EMIT_CALL(BPF_FUNC_sk_fullsock), - BPF_MOV64_REG(BPF_REG_7, BPF_REG_0), - /* r0 = sk_fullsock(skb); */ - BPF_MOV64_REG(BPF_REG_1, BPF_REG_6), - BPF_EMIT_CALL(BPF_FUNC_sk_fullsock), - /* if (r0 == null) return 0; */ - BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 2), - /* if (r0 == r7) r0 = *(r7->type); */ - BPF_JMP_REG(BPF_JNE, BPF_REG_0, BPF_REG_7, 1), /* Use ! JNE ! */ - BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_7, offsetof(struct bpf_sock, type)), - /* return 0 */ - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_EXIT_INSN(), - }, - .prog_type = BPF_PROG_TYPE_CGROUP_SKB, - .result = ACCEPT, - .result_unpriv = REJECT, - .errstr_unpriv = "R7 pointer comparison", -}, -{ - /* Same as above, but verify that another branch of JNE still - * prohibits access to PTR_MAYBE_NULL. - */ - "jne/jeq infer not null, PTR_TO_SOCKET_OR_NULL unchanged for JNE true branch", - .insns = { - /* r6 = skb->sk */ - BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, offsetof(struct __sk_buff, sk)), - /* if (r6 == 0) return 0; */ - BPF_JMP_IMM(BPF_JEQ, BPF_REG_6, 0, 9), - /* r7 = sk_fullsock(skb); */ - BPF_MOV64_REG(BPF_REG_1, BPF_REG_6), - BPF_EMIT_CALL(BPF_FUNC_sk_fullsock), - BPF_MOV64_REG(BPF_REG_7, BPF_REG_0), - /* r0 = sk_fullsock(skb); */ - BPF_MOV64_REG(BPF_REG_1, BPF_REG_6), - BPF_EMIT_CALL(BPF_FUNC_sk_fullsock), - /* if (r0 == null) return 0; */ - BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 3), - /* if (r0 == r7) return 0; */ - BPF_JMP_REG(BPF_JNE, BPF_REG_0, BPF_REG_7, 1), /* Use ! JNE ! */ - BPF_JMP_IMM(BPF_JA, 0, 0, 1), - /* r0 = *(r7->type); */ - BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_7, offsetof(struct bpf_sock, type)), - /* return 0 */ - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_EXIT_INSN(), - }, - .prog_type = BPF_PROG_TYPE_CGROUP_SKB, - .result = REJECT, - .errstr = "R7 invalid mem access 'sock_or_null'", - .result_unpriv = REJECT, - .errstr_unpriv = "R7 pointer comparison", -}, -{ - /* Same as a first test, but not null should be inferred for JEQ branch */ - "jne/jeq infer not null, PTR_TO_SOCKET_OR_NULL -> PTR_TO_SOCKET for JEQ true branch", - .insns = { - /* r6 = skb->sk; */ - BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, offsetof(struct __sk_buff, sk)), - /* if (r6 == null) return 0; */ - BPF_JMP_IMM(BPF_JEQ, BPF_REG_6, 0, 9), - /* r7 = sk_fullsock(skb); */ - BPF_MOV64_REG(BPF_REG_1, BPF_REG_6), - BPF_EMIT_CALL(BPF_FUNC_sk_fullsock), - BPF_MOV64_REG(BPF_REG_7, BPF_REG_0), - /* r0 = sk_fullsock(skb); */ - BPF_MOV64_REG(BPF_REG_1, BPF_REG_6), - BPF_EMIT_CALL(BPF_FUNC_sk_fullsock), - /* if (r0 == null) return 0; */ - BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 3), - /* if (r0 != r7) return 0; */ - BPF_JMP_REG(BPF_JEQ, BPF_REG_0, BPF_REG_7, 1), /* Use ! JEQ ! */ - BPF_JMP_IMM(BPF_JA, 0, 0, 1), - /* r0 = *(r7->type); */ - BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_7, offsetof(struct bpf_sock, type)), - /* return 0; */ - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_EXIT_INSN(), - }, - .prog_type = BPF_PROG_TYPE_CGROUP_SKB, - .result = ACCEPT, - .result_unpriv = REJECT, - .errstr_unpriv = "R7 pointer comparison", -}, -{ - /* Same as above, but verify that another branch of JNE still - * prohibits access to PTR_MAYBE_NULL. - */ - "jne/jeq infer not null, PTR_TO_SOCKET_OR_NULL unchanged for JEQ false branch", - .insns = { - /* r6 = skb->sk; */ - BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, offsetof(struct __sk_buff, sk)), - /* if (r6 == null) return 0; */ - BPF_JMP_IMM(BPF_JEQ, BPF_REG_6, 0, 8), - /* r7 = sk_fullsock(skb); */ - BPF_MOV64_REG(BPF_REG_1, BPF_REG_6), - BPF_EMIT_CALL(BPF_FUNC_sk_fullsock), - BPF_MOV64_REG(BPF_REG_7, BPF_REG_0), - /* r0 = sk_fullsock(skb); */ - BPF_MOV64_REG(BPF_REG_1, BPF_REG_6), - BPF_EMIT_CALL(BPF_FUNC_sk_fullsock), - /* if (r0 == null) return 0; */ - BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 2), - /* if (r0 != r7) r0 = *(r7->type); */ - BPF_JMP_REG(BPF_JEQ, BPF_REG_0, BPF_REG_7, 1), /* Use ! JEQ ! */ - BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_7, offsetof(struct bpf_sock, type)), - /* return 0; */ - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_EXIT_INSN(), - }, - .prog_type = BPF_PROG_TYPE_CGROUP_SKB, - .result = REJECT, - .errstr = "R7 invalid mem access 'sock_or_null'", - .result_unpriv = REJECT, - .errstr_unpriv = "R7 pointer comparison", -}, -{ - /* Maps are treated in a different branch of `mark_ptr_not_null_reg`, - * so separate test for maps case. - */ - "jne/jeq infer not null, PTR_TO_MAP_VALUE_OR_NULL -> PTR_TO_MAP_VALUE", - .insns = { - /* r9 = &some stack to use as key */ - BPF_ST_MEM(BPF_W, BPF_REG_10, -8, 0), - BPF_MOV64_REG(BPF_REG_9, BPF_REG_10), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_9, -8), - /* r8 = process local map */ - BPF_LD_MAP_FD(BPF_REG_8, 0), - /* r6 = map_lookup_elem(r8, r9); */ - BPF_MOV64_REG(BPF_REG_1, BPF_REG_8), - BPF_MOV64_REG(BPF_REG_2, BPF_REG_9), - BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), - BPF_MOV64_REG(BPF_REG_6, BPF_REG_0), - /* r7 = map_lookup_elem(r8, r9); */ - BPF_MOV64_REG(BPF_REG_1, BPF_REG_8), - BPF_MOV64_REG(BPF_REG_2, BPF_REG_9), - BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), - BPF_MOV64_REG(BPF_REG_7, BPF_REG_0), - /* if (r6 == 0) return 0; */ - BPF_JMP_IMM(BPF_JEQ, BPF_REG_6, 0, 2), - /* if (r6 != r7) return 0; */ - BPF_JMP_REG(BPF_JNE, BPF_REG_6, BPF_REG_7, 1), - /* read *r7; */ - BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_7, offsetof(struct bpf_xdp_sock, queue_id)), - /* return 0; */ - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_EXIT_INSN(), - }, - .fixup_map_xskmap = { 3 }, - .prog_type = BPF_PROG_TYPE_XDP, - .result = ACCEPT, -},