From patchwork Wed Mar 19 17:30:08 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Anton Protopopov X-Patchwork-Id: 14022870 X-Patchwork-Delegate: bpf@iogearbox.net Received: from mail-wr1-f53.google.com (mail-wr1-f53.google.com [209.85.221.53]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E747D1EB5EE for ; Wed, 19 Mar 2025 17:26:34 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.53 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1742405197; cv=none; b=dpIG/zlK+QZLB0zpVrUditBXAHM+qN+ATx4ovjJzfiZifM3xdV4YrOa6PN7CDDrZaC9IaAFbtGy9BwJqAdFZ9JHiXvbRqYoZsLV25BKzyql6lT6uIY6GAhooADZ+32Gdq7pEiF2aDpol78fyvue4l1H6I3Qyx0hgtOU4LtaPJ8Y= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1742405197; c=relaxed/simple; bh=nov4n6XsPtCtLzG0tAtjgkKvuoVFkOW0Ybq36hTkimM=; h=From:To:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=DyqjMN32OkTLxxYm8vxdpsRUTJpFYvDtbXD/YDh2tHAjFsVaaLqeP+OSbWwxpr4p1/3zpINWzeeUeb7Ve2sUrlnCTWyddt/xZdnDA2VOQgPMFF82h2TSVZzSK9pPg7BOF4FwFbR5OmZSWlDWta4Um1FqbmpxpA+IA5sPm1KWBiw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=isovalent.com; spf=pass smtp.mailfrom=isovalent.com; dkim=pass (2048-bit key) header.d=isovalent.com header.i=@isovalent.com header.b=QX1kIDPa; arc=none smtp.client-ip=209.85.221.53 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=isovalent.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=isovalent.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=isovalent.com header.i=@isovalent.com header.b="QX1kIDPa" Received: by mail-wr1-f53.google.com with SMTP id ffacd0b85a97d-39141ffa9fcso6208510f8f.0 for ; Wed, 19 Mar 2025 10:26:34 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=isovalent.com; s=google; t=1742405193; x=1743009993; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:from:to:cc:subject:date:message-id :reply-to; bh=x6I1ssH+WyA5ZAOe91vhZ5yhn2QxkHd3Zo84GLev48Q=; b=QX1kIDPaSQviJLeuzQyh10jOFnqRV5/xwbZO4e6WHP8grPVN4/psMmWkiQs6wZVgkN 939JIxJMFLeyf+QzIRG+N0S5EkVzJaOfI6XI9N94V0DkpNzUvk4H/cHslSB59D/pU9BJ +x5isgywIEhkVsk1VGMHcyX5exp+ZklKgf2EaFOPDBoElRflggQ4XuwecStFXSPQOrIU jawcK+9BWu4/pMgOhPCSS4yL1g80GT5DRznX+4gNiaan3aCKAyLk/L1xB6LC8QVULuxt NUzeloRFhGSy8UHT/7XitHaq77jPLqCEEi/4eh3hFNatciiHFRHZbJGqhFiqStQ9hbop Ddow== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1742405193; x=1743009993; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=x6I1ssH+WyA5ZAOe91vhZ5yhn2QxkHd3Zo84GLev48Q=; b=NXFLM/h8kclrOSqUuyHnSUjf9A+WksmG6ge5h1M65IkYoScgzMMUCLAX6YErjSGb39 PU7PFBZ3S6X6ZYhAFIMg6H11P4MvF30+Z8SW0LP4RehYtXmsfBMqMvhpEsnKppGVGMu1 KZlV71CTmcPD83vDRDhcKTuRkGBm5jhvoo/7LHkiPDLX3ODuMLAdMWfEsi01940U/Lix 0YiCnX2MI7FB+Gw/xT3HJBDD3GDP1jh7LHEUdR9EuG2RSForztNgnXsyoKHjlxsR2BGe dwHQ2dlHYytZ7DTgsGII5fJX2cSBpZe0kicEcpoRmfnA0sPyG3AMcYg3tkPyVaKfw/vt zq+Q== X-Forwarded-Encrypted: i=1; AJvYcCVEl7Vdw8ewdwQuHhnXWzvWAC8r1yu5Rc5cRAK3W0uycZkPbDiM53ddIK/XXee9rThDMlY=@vger.kernel.org X-Gm-Message-State: AOJu0YzH2jd80/48zyzNpznr9vEmjc/7HKEX1IfPZCz3pnNilFRMvz1u csQWYG0wVfNY1RJAsHVEO91eqt+nFbrUHYTpBkPZaH7KktwjMQOSplH0qF2xLeuUJmNeQgyRC1k a X-Gm-Gg: ASbGncvn4mnbh9G/cMY+qh+ojTFUgi2ktmRajj/62C8CuszUBQW6tvt+dr5q5T4vn/4 WWgyG0Ft7hw4nq1enWL/X6DggV9EzYrgzMsyGt7wztFnMgy9ZxiKsC4z6oiebLSlqLvk8Rn/G0w RM/SpZq/otYj2iiHvFlyU+RGH0L57F1ugyNIcxkDv7K/WS5+CgVCR4QGgbELEbGp9bDR0bXDQao 4ppt1Iov1OIJ1BymtQs25Friivzd2V2A97zKfJvABhGrovDBJIGED1zd5/htswRkRH2UBO65NeW j/3rd6PP3BvzaqvStdRZsATOIMDDoTx3PFJ/Kti1zOGwOIE0uvif/alS9w== X-Google-Smtp-Source: AGHT+IF9o1l9b2bj2RIQBo8WbhAOfqmKeSpTQPxDfSQnpv8ZFm/ybS2WLGbpvgfm5FqWUCDJ6ZauRg== X-Received: by 2002:a5d:64c5:0:b0:390:e62e:f31f with SMTP id ffacd0b85a97d-399739b4353mr2809244f8f.3.1742405192951; Wed, 19 Mar 2025 10:26:32 -0700 (PDT) Received: from localhost.localdomain ([2a04:ee41:4:b2de:1ac0:4dff:fe0f:3782]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-395c7df35ecsm21743415f8f.16.2025.03.19.10.26.32 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 19 Mar 2025 10:26:32 -0700 (PDT) From: Anton Protopopov To: Yonghong Song , bpf@vger.kernel.org, Andrii Nakryiko , Daniel Borkmann , Eduard Zingerman , Quentin Monnet , Anton Protopopov , Alexei Starovoitov Subject: [RFC PATCH bpf-next 03/14] selftests/bpf: add selftests for new insn_set map Date: Wed, 19 Mar 2025 17:30:08 +0000 Message-Id: <20250319173008.1029283-1-aspsk@isovalent.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: References: Precedence: bulk X-Mailing-List: bpf@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Patchwork-Delegate: bpf@iogearbox.net X-Patchwork-State: RFC Tests are split in two parts. The `bpf_insn_set_ops` test checks that the map is managed properly: * Incorrect instruction indexes are rejected * Non-sorted and non-unique indexes are rejected * Unfrozen maps are not accepted * Two programs can't use the same map * BPF progs can't operate the map The `bpf_insn_set_reloc` part validates, as best as it can do it from user space, that instructions are relocated properly: * no relocations => map is the same * expected relocations when instructions are added * expected relocations when instructions are deleted * expected relocations when multiple functions are present Signed-off-by: Anton Protopopov --- .../selftests/bpf/prog_tests/bpf_insn_set.c | 556 ++++++++++++++++++ 1 file changed, 556 insertions(+) create mode 100644 tools/testing/selftests/bpf/prog_tests/bpf_insn_set.c diff --git a/tools/testing/selftests/bpf/prog_tests/bpf_insn_set.c b/tools/testing/selftests/bpf/prog_tests/bpf_insn_set.c new file mode 100644 index 000000000000..9a9d9f1e9885 --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/bpf_insn_set.c @@ -0,0 +1,556 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include + +static int map_create(__u32 map_type, __u32 max_entries) +{ + const char *map_name = "insn_set"; + __u32 key_size = 4; + __u32 value_size = 4; + + return bpf_map_create(map_type, map_name, key_size, value_size, max_entries, NULL); +} + +static int prog_load(struct bpf_insn *insns, __u32 insn_cnt, int *fd_array, __u32 fd_array_cnt) +{ + LIBBPF_OPTS(bpf_prog_load_opts, opts); + + opts.fd_array = fd_array; + opts.fd_array_cnt = fd_array_cnt; + + return bpf_prog_load(BPF_PROG_TYPE_XDP, NULL, "GPL", insns, insn_cnt, &opts); +} + +/* + * Load a program, which will not be anyhow mangled by the verifier. Add an + * insn_set map pointing to every instruction. Check that it hasn't changed + * after the program load. + */ +static void check_one_to_one_mapping(void) +{ + struct bpf_insn insns[] = { + BPF_MOV64_IMM(BPF_REG_0, 4), + BPF_MOV64_IMM(BPF_REG_0, 3), + BPF_MOV64_IMM(BPF_REG_0, 2), + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }; + int prog_fd = -1, map_fd; + int i; + + map_fd = map_create(BPF_MAP_TYPE_INSN_SET, ARRAY_SIZE(insns)); + if (!ASSERT_GE(map_fd, 0, "map_create")) + return; + + for (i = 0; i < ARRAY_SIZE(insns); i++) + if (!ASSERT_EQ(bpf_map_update_elem(map_fd, &i, &i, 0), 0, "bpf_map_update_elem")) + goto cleanup; + + if (!ASSERT_EQ(bpf_map_freeze(map_fd), 0, "bpf_map_freeze")) + goto cleanup; + + prog_fd = prog_load(insns, ARRAY_SIZE(insns), &map_fd, 1); + if (!ASSERT_GE(prog_fd, 0, "bpf(BPF_PROG_LOAD)")) + goto cleanup; + + for (i = 0; i < ARRAY_SIZE(insns); i++) { + __u32 val; + + if (!ASSERT_EQ(bpf_map_lookup_elem(map_fd, &i, &val), 0, "bpf_map_lookup_elem")) + goto cleanup; + + ASSERT_EQ(val, i, "val should be equal i"); + } + +cleanup: + close(prog_fd); + close(map_fd); +} + +/* + * Try to load a program with a map which points to outside of the program + */ +static void check_out_of_bounds_index(void) +{ + struct bpf_insn insns[] = { + BPF_MOV64_IMM(BPF_REG_0, 4), + BPF_MOV64_IMM(BPF_REG_0, 3), + BPF_MOV64_IMM(BPF_REG_0, 2), + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }; + int prog_fd, map_fd; + int key, val; + + map_fd = map_create(BPF_MAP_TYPE_INSN_SET, 1); + if (!ASSERT_GE(map_fd, 0, "map_create")) + return; + + key = 0; + val = ARRAY_SIZE(insns); /* too big */ + if (!ASSERT_EQ(bpf_map_update_elem(map_fd, &key, &val, 0), 0, "bpf_map_update_elem")) + goto cleanup; + + if (!ASSERT_EQ(bpf_map_freeze(map_fd), 0, "bpf_map_freeze")) + goto cleanup; + + errno = 0; + prog_fd = prog_load(insns, ARRAY_SIZE(insns), &map_fd, 1); + if (!ASSERT_EQ(prog_fd, -EINVAL, "program should have been rejected (prog_fd != -EINVAL)")) { + close(prog_fd); + goto cleanup; + } + +cleanup: + close(map_fd); +} + +/* + * Try to load a program with a map which points to the middle of 16-bit insn + */ +static void check_mid_insn_index(void) +{ + struct bpf_insn insns[] = { + BPF_LD_IMM64(BPF_REG_0, 0), /* 2 x 8 */ + BPF_EXIT_INSN(), + }; + int prog_fd, map_fd; + int key, val; + + map_fd = map_create(BPF_MAP_TYPE_INSN_SET, 1); + if (!ASSERT_GE(map_fd, 0, "map_create")) + return; + + key = 0; + val = 1; /* middle of 16-byte instruction */ + if (!ASSERT_EQ(bpf_map_update_elem(map_fd, &key, &val, 0), 0, "bpf_map_update_elem")) + goto cleanup; + + if (!ASSERT_EQ(bpf_map_freeze(map_fd), 0, "bpf_map_freeze")) + goto cleanup; + + errno = 0; + prog_fd = prog_load(insns, ARRAY_SIZE(insns), &map_fd, 1); + if (!ASSERT_EQ(prog_fd, -EINVAL, "program should have been rejected (prog_fd != -EINVAL)")) { + close(prog_fd); + goto cleanup; + } + +cleanup: + close(map_fd); +} + +static void check_incorrect_index(void) +{ + check_out_of_bounds_index(); + check_mid_insn_index(); +} + +static void check_not_sorted(void) +{ + struct bpf_insn insns[] = { + BPF_MOV64_IMM(BPF_REG_0, 4), + BPF_MOV64_IMM(BPF_REG_0, 3), + BPF_MOV64_IMM(BPF_REG_0, 2), + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }; + int prog_fd, map_fd; + int i, val; + + map_fd = map_create(BPF_MAP_TYPE_INSN_SET, ARRAY_SIZE(insns)); + if (!ASSERT_GE(map_fd, 0, "map_create")) + return; + + for (i = 0; i < ARRAY_SIZE(insns); i++) { + val = ARRAY_SIZE(insns) - i - 1; /* reverse indexes */ + if (!ASSERT_EQ(bpf_map_update_elem(map_fd, &i, &val, 0), 0, "bpf_map_update_elem")) + goto cleanup; + } + + if (!ASSERT_EQ(bpf_map_freeze(map_fd), 0, "bpf_map_freeze")) + goto cleanup; + + errno = 0; + prog_fd = prog_load(insns, ARRAY_SIZE(insns), &map_fd, 1); + if (!ASSERT_EQ(prog_fd, -EINVAL, "program should have been rejected (prog_fd != -EINVAL)")) { + close(prog_fd); + goto cleanup; + } + +cleanup: + close(map_fd); +} + +static void check_not_unique(void) +{ + struct bpf_insn insns[] = { + BPF_MOV64_IMM(BPF_REG_0, 4), + BPF_MOV64_IMM(BPF_REG_0, 3), + BPF_MOV64_IMM(BPF_REG_0, 2), + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }; + int prog_fd, map_fd; + int i, val; + + map_fd = map_create(BPF_MAP_TYPE_INSN_SET, ARRAY_SIZE(insns)); + if (!ASSERT_GE(map_fd, 0, "map_create")) + return; + + for (i = 0; i < ARRAY_SIZE(insns); i++) { + val = 1; + if (!ASSERT_EQ(bpf_map_update_elem(map_fd, &i, &val, 0), 0, "bpf_map_update_elem")) + goto cleanup; + } + + if (!ASSERT_EQ(bpf_map_freeze(map_fd), 0, "bpf_map_freeze")) + goto cleanup; + + errno = 0; + prog_fd = prog_load(insns, ARRAY_SIZE(insns), &map_fd, 1); + if (!ASSERT_EQ(prog_fd, -EINVAL, "program should have been rejected (prog_fd != -EINVAL)")) { + close(prog_fd); + goto cleanup; + } + +cleanup: + close(map_fd); +} + +static void check_not_sorted_or_unique(void) +{ + check_not_sorted(); + check_not_unique(); +} + +/* + * Load a program with two patches (get jiffies, for simplicity). Add an + * insn_set map pointing to every instruction. Check how it was relocated + * after the program load. + */ +static void check_relocate_simple(void) +{ + struct bpf_insn insns[] = { + BPF_MOV64_IMM(BPF_REG_0, 2), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_jiffies64), + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_jiffies64), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }; + int prog_fd = -1, map_fd; + __u32 map_in[] = {0, 1, 2, 3, 4, 5}; + __u32 map_out[] = {0, 1, 4, 5, 8, 9}; + int i; + + map_fd = map_create(BPF_MAP_TYPE_INSN_SET, ARRAY_SIZE(insns)); + if (!ASSERT_GE(map_fd, 0, "map_create")) + return; + + for (i = 0; i < ARRAY_SIZE(insns); i++) + if (!ASSERT_EQ(bpf_map_update_elem(map_fd, &i, &map_in[i], 0), 0, + "bpf_map_update_elem")) + goto cleanup; + + if (!ASSERT_EQ(bpf_map_freeze(map_fd), 0, "bpf_map_freeze")) + goto cleanup; + + prog_fd = prog_load(insns, ARRAY_SIZE(insns), &map_fd, 1); + if (!ASSERT_GE(prog_fd, 0, "bpf(BPF_PROG_LOAD)")) + goto cleanup; + + for (i = 0; i < ARRAY_SIZE(insns); i++) { + __u32 val; + + if (!ASSERT_EQ(bpf_map_lookup_elem(map_fd, &i, &val), 0, "bpf_map_lookup_elem")) + goto cleanup; + + ASSERT_EQ(val, map_out[i], "val should be equal map_out[i]"); + } + +cleanup: + close(prog_fd); + close(map_fd); +} + +/* + * Verifier can delete code in two cases: nops & dead code. From the relocation + * point of view, the two cases look the same, so test using the simplest + * method: by loading some nops + */ +static void check_relocate_deletions(void) +{ + struct bpf_insn insns[] = { + BPF_MOV64_IMM(BPF_REG_0, 2), + BPF_JMP_IMM(BPF_JA, 0, 0, 0), /* nop */ + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_JMP_IMM(BPF_JA, 0, 0, 0), /* nop */ + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }; + int prog_fd = -1, map_fd; + __u32 map_in[] = {0, 1, 2, 3, 4, 5}; + __u32 map_out[] = {0, -1, 1, -1, 2, 3}; + int i; + + map_fd = map_create(BPF_MAP_TYPE_INSN_SET, ARRAY_SIZE(insns)); + if (!ASSERT_GE(map_fd, 0, "map_create")) + return; + + for (i = 0; i < ARRAY_SIZE(insns); i++) + if (!ASSERT_EQ(bpf_map_update_elem(map_fd, &i, &map_in[i], 0), 0, + "bpf_map_update_elem")) + goto cleanup; + + if (!ASSERT_EQ(bpf_map_freeze(map_fd), 0, "bpf_map_freeze")) + goto cleanup; + + prog_fd = prog_load(insns, ARRAY_SIZE(insns), &map_fd, 1); + if (!ASSERT_GE(prog_fd, 0, "bpf(BPF_PROG_LOAD)")) + goto cleanup; + + for (i = 0; i < ARRAY_SIZE(insns); i++) { + __u32 val; + + if (!ASSERT_EQ(bpf_map_lookup_elem(map_fd, &i, &val), 0, "bpf_map_lookup_elem")) + goto cleanup; + + ASSERT_EQ(val, map_out[i], "val should be equal map_out[i]"); + } + +cleanup: + close(prog_fd); + close(map_fd); +} + +static void check_relocate_with_functions(void) +{ + struct bpf_insn insns[] = { + BPF_JMP_IMM(BPF_JA, 0, 0, 0), /* nop */ + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_jiffies64), + BPF_JMP_IMM(BPF_JA, 0, 0, 0), /* nop */ + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 2), + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_EXIT_INSN(), + BPF_JMP_IMM(BPF_JA, 0, 0, 0), /* nop */ + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_jiffies64), + BPF_JMP_IMM(BPF_JA, 0, 0, 0), /* nop */ + BPF_MOV64_IMM(BPF_REG_0, 2), + BPF_EXIT_INSN(), + }; + int prog_fd = -1, map_fd; + __u32 map_in[] = { 0, 1, 2, 3, 4, 5, /* func */ 6, 7, 8, 9, 10}; + __u32 map_out[] = {-1, 0, -1, 3, 4, 5, /* func */ -1, 6, -1, 9, 10}; + int i; + + map_fd = map_create(BPF_MAP_TYPE_INSN_SET, ARRAY_SIZE(insns)); + if (!ASSERT_GE(map_fd, 0, "map_create")) + return; + + for (i = 0; i < ARRAY_SIZE(insns); i++) + if (!ASSERT_EQ(bpf_map_update_elem(map_fd, &i, &map_in[i], 0), 0, + "bpf_map_update_elem")) + goto cleanup; + + if (!ASSERT_EQ(bpf_map_freeze(map_fd), 0, "bpf_map_freeze")) + goto cleanup; + + prog_fd = prog_load(insns, ARRAY_SIZE(insns), &map_fd, 1); + if (!ASSERT_GE(prog_fd, 0, "bpf(BPF_PROG_LOAD)")) + goto cleanup; + + for (i = 0; i < ARRAY_SIZE(insns); i++) { + __u32 val; + + if (!ASSERT_EQ(bpf_map_lookup_elem(map_fd, &i, &val), 0, "bpf_map_lookup_elem")) + goto cleanup; + + ASSERT_EQ(val, map_out[i], "val should be equal map_out[i]"); + } + +cleanup: + close(prog_fd); + close(map_fd); +} + +/* Once map was initialized, it should be frozen */ +static void check_load_unfrozen_map(void) +{ + struct bpf_insn insns[] = { + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }; + int prog_fd = -1, map_fd; + int i; + + map_fd = map_create(BPF_MAP_TYPE_INSN_SET, ARRAY_SIZE(insns)); + if (!ASSERT_GE(map_fd, 0, "map_create")) + return; + + for (i = 0; i < ARRAY_SIZE(insns); i++) + if (!ASSERT_EQ(bpf_map_update_elem(map_fd, &i, &i, 0), 0, "bpf_map_update_elem")) + goto cleanup; + + errno = 0; + prog_fd = prog_load(insns, ARRAY_SIZE(insns), &map_fd, 1); + if (!ASSERT_EQ(prog_fd, -EINVAL, "program should have been rejected (prog_fd != -EINVAL)")) + goto cleanup; + + /* correctness: now freeze the map, the program should load fine */ + + if (!ASSERT_EQ(bpf_map_freeze(map_fd), 0, "bpf_map_freeze")) + goto cleanup; + + prog_fd = prog_load(insns, ARRAY_SIZE(insns), &map_fd, 1); + if (!ASSERT_GE(prog_fd, 0, "bpf(BPF_PROG_LOAD)")) + goto cleanup; + + for (i = 0; i < ARRAY_SIZE(insns); i++) { + __u32 val; + + if (!ASSERT_EQ(bpf_map_lookup_elem(map_fd, &i, &val), 0, "bpf_map_lookup_elem")) + goto cleanup; + + ASSERT_EQ(val, i, "val should be equal i"); + } + +cleanup: + close(prog_fd); + close(map_fd); +} + +/* Map can be used only by one BPF program */ +static void check_no_map_reuse(void) +{ + struct bpf_insn insns[] = { + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }; + int prog_fd = -1, map_fd, extra_fd = -1; + int i; + + map_fd = map_create(BPF_MAP_TYPE_INSN_SET, ARRAY_SIZE(insns)); + if (!ASSERT_GE(map_fd, 0, "map_create")) + return; + + for (i = 0; i < ARRAY_SIZE(insns); i++) + if (!ASSERT_EQ(bpf_map_update_elem(map_fd, &i, &i, 0), 0, "bpf_map_update_elem")) + goto cleanup; + + if (!ASSERT_EQ(bpf_map_freeze(map_fd), 0, "bpf_map_freeze")) + goto cleanup; + + prog_fd = prog_load(insns, ARRAY_SIZE(insns), &map_fd, 1); + if (!ASSERT_GE(prog_fd, 0, "bpf(BPF_PROG_LOAD)")) + goto cleanup; + + for (i = 0; i < ARRAY_SIZE(insns); i++) { + __u32 val; + + if (!ASSERT_EQ(bpf_map_lookup_elem(map_fd, &i, &val), 0, "bpf_map_lookup_elem")) + goto cleanup; + + ASSERT_EQ(val, i, "val should be equal i"); + } + + errno = 0; + extra_fd = prog_load(insns, ARRAY_SIZE(insns), &map_fd, 1); + if (!ASSERT_EQ(extra_fd, -EBUSY, "program should have been rejected (extra_fd != -EBUSY)")) + goto cleanup; + + /* correctness: check that prog is still loadable without fd_array */ + extra_fd = prog_load(insns, ARRAY_SIZE(insns), NULL, 0); + if (!ASSERT_GE(prog_fd, 0, "bpf(BPF_PROG_LOAD): expected no error")) + goto cleanup; + +cleanup: + close(extra_fd); + close(prog_fd); + close(map_fd); +} + +static void check_bpf_no_lookup(void) +{ + struct bpf_insn insns[] = { + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), + BPF_EXIT_INSN(), + }; + int prog_fd = -1, map_fd; + + map_fd = map_create(BPF_MAP_TYPE_INSN_SET, 1); + if (!ASSERT_GE(map_fd, 0, "map_create")) + return; + + /* otherwise will be rejected as unfrozen */ + if (!ASSERT_EQ(bpf_map_freeze(map_fd), 0, "bpf_map_freeze")) + goto cleanup; + + insns[0].imm = map_fd; + + errno = 0; + prog_fd = prog_load(insns, ARRAY_SIZE(insns), NULL, 0); + if (!ASSERT_EQ(prog_fd, -EINVAL, "program should have been rejected (prog_fd != -EINVAL)")) + goto cleanup; + + /* correctness: check that prog is still loadable with normal map */ + close(map_fd); + map_fd = map_create(BPF_MAP_TYPE_ARRAY, 1); + insns[0].imm = map_fd; + prog_fd = prog_load(insns, ARRAY_SIZE(insns), NULL, 0); + if (!ASSERT_GE(prog_fd, 0, "bpf(BPF_PROG_LOAD)")) + goto cleanup; + +cleanup: + close(prog_fd); + close(map_fd); +} + +static void check_bpf_side(void) +{ + check_bpf_no_lookup(); +} + +/* Test how relocations work */ +void test_bpf_insn_set_reloc(void) +{ + if (test__start_subtest("one2one")) + check_one_to_one_mapping(); + + if (test__start_subtest("relocate-simple")) + check_relocate_simple(); + + if (test__start_subtest("relocate-deletions")) + check_relocate_deletions(); + + if (test__start_subtest("relocate-multiple-functions")) + check_relocate_with_functions(); +} + +/* Check all kinds of operations and related restrictions */ +void test_bpf_insn_set_ops(void) +{ + if (test__start_subtest("incorrect-index")) + check_incorrect_index(); + + if (test__start_subtest("not-sorted-or-unique")) + check_not_sorted_or_unique(); + + if (test__start_subtest("load-unfrozen-map")) + check_load_unfrozen_map(); + + if (test__start_subtest("no-map-reuse")) + check_no_map_reuse(); + + if (test__start_subtest("bpf-side-ops")) + check_bpf_side(); +}