1218edd6dSAnton Protopopov // SPDX-License-Identifier: GPL-2.0 2218edd6dSAnton Protopopov 3218edd6dSAnton Protopopov #include <bpf/bpf.h> 4218edd6dSAnton Protopopov #include <test_progs.h> 5218edd6dSAnton Protopopov 6218edd6dSAnton Protopopov #ifdef __x86_64__ 7218edd6dSAnton Protopopov static int map_create(__u32 map_type, __u32 max_entries) 8218edd6dSAnton Protopopov { 9218edd6dSAnton Protopopov const char *map_name = "insn_array"; 10218edd6dSAnton Protopopov __u32 key_size = 4; 11218edd6dSAnton Protopopov __u32 value_size = sizeof(struct bpf_insn_array_value); 12218edd6dSAnton Protopopov 13218edd6dSAnton Protopopov return bpf_map_create(map_type, map_name, key_size, value_size, max_entries, NULL); 14218edd6dSAnton Protopopov } 15218edd6dSAnton Protopopov 16218edd6dSAnton Protopopov static int prog_load(struct bpf_insn *insns, __u32 insn_cnt, int *fd_array, __u32 fd_array_cnt) 17218edd6dSAnton Protopopov { 18218edd6dSAnton Protopopov LIBBPF_OPTS(bpf_prog_load_opts, opts); 19218edd6dSAnton Protopopov 20218edd6dSAnton Protopopov opts.fd_array = fd_array; 21218edd6dSAnton Protopopov opts.fd_array_cnt = fd_array_cnt; 22218edd6dSAnton Protopopov 23218edd6dSAnton Protopopov return bpf_prog_load(BPF_PROG_TYPE_XDP, NULL, "GPL", insns, insn_cnt, &opts); 24218edd6dSAnton Protopopov } 25218edd6dSAnton Protopopov 26218edd6dSAnton Protopopov static void __check_success(struct bpf_insn *insns, __u32 insn_cnt, __u32 *map_in, __u32 *map_out) 27218edd6dSAnton Protopopov { 28218edd6dSAnton Protopopov struct bpf_insn_array_value val = {}; 29218edd6dSAnton Protopopov int prog_fd = -1, map_fd, i; 30218edd6dSAnton Protopopov 31218edd6dSAnton Protopopov map_fd = map_create(BPF_MAP_TYPE_INSN_ARRAY, insn_cnt); 32218edd6dSAnton Protopopov if (!ASSERT_GE(map_fd, 0, "map_create")) 33218edd6dSAnton Protopopov return; 34218edd6dSAnton Protopopov 35218edd6dSAnton Protopopov for (i = 0; i < insn_cnt; i++) { 36218edd6dSAnton Protopopov val.orig_off = map_in[i]; 37218edd6dSAnton Protopopov if (!ASSERT_EQ(bpf_map_update_elem(map_fd, &i, &val, 0), 0, "bpf_map_update_elem")) 38218edd6dSAnton Protopopov goto cleanup; 39218edd6dSAnton Protopopov } 40218edd6dSAnton Protopopov 41218edd6dSAnton Protopopov if (!ASSERT_EQ(bpf_map_freeze(map_fd), 0, "bpf_map_freeze")) 42218edd6dSAnton Protopopov goto cleanup; 43218edd6dSAnton Protopopov 44218edd6dSAnton Protopopov prog_fd = prog_load(insns, insn_cnt, &map_fd, 1); 45218edd6dSAnton Protopopov if (!ASSERT_GE(prog_fd, 0, "bpf(BPF_PROG_LOAD)")) 46218edd6dSAnton Protopopov goto cleanup; 47218edd6dSAnton Protopopov 48218edd6dSAnton Protopopov for (i = 0; i < insn_cnt; i++) { 49218edd6dSAnton Protopopov char buf[64]; 50218edd6dSAnton Protopopov 51218edd6dSAnton Protopopov if (!ASSERT_EQ(bpf_map_lookup_elem(map_fd, &i, &val), 0, "bpf_map_lookup_elem")) 52218edd6dSAnton Protopopov goto cleanup; 53218edd6dSAnton Protopopov 54218edd6dSAnton Protopopov snprintf(buf, sizeof(buf), "val.xlated_off should be equal map_out[%d]", i); 55218edd6dSAnton Protopopov ASSERT_EQ(val.xlated_off, map_out[i], buf); 56218edd6dSAnton Protopopov } 57218edd6dSAnton Protopopov 58218edd6dSAnton Protopopov cleanup: 59218edd6dSAnton Protopopov close(prog_fd); 60218edd6dSAnton Protopopov close(map_fd); 61218edd6dSAnton Protopopov } 62218edd6dSAnton Protopopov 63218edd6dSAnton Protopopov /* 64218edd6dSAnton Protopopov * Load a program, which will not be anyhow mangled by the verifier. Add an 65218edd6dSAnton Protopopov * insn_array map pointing to every instruction. Check that it hasn't changed 66218edd6dSAnton Protopopov * after the program load. 67218edd6dSAnton Protopopov */ 68218edd6dSAnton Protopopov static void check_one_to_one_mapping(void) 69218edd6dSAnton Protopopov { 70218edd6dSAnton Protopopov struct bpf_insn insns[] = { 71218edd6dSAnton Protopopov BPF_MOV64_IMM(BPF_REG_0, 4), 72218edd6dSAnton Protopopov BPF_MOV64_IMM(BPF_REG_0, 3), 73218edd6dSAnton Protopopov BPF_MOV64_IMM(BPF_REG_0, 2), 74218edd6dSAnton Protopopov BPF_MOV64_IMM(BPF_REG_0, 1), 75218edd6dSAnton Protopopov BPF_MOV64_IMM(BPF_REG_0, 0), 76218edd6dSAnton Protopopov BPF_EXIT_INSN(), 77218edd6dSAnton Protopopov }; 78218edd6dSAnton Protopopov __u32 map_in[] = {0, 1, 2, 3, 4, 5}; 79218edd6dSAnton Protopopov __u32 map_out[] = {0, 1, 2, 3, 4, 5}; 80218edd6dSAnton Protopopov 81218edd6dSAnton Protopopov __check_success(insns, ARRAY_SIZE(insns), map_in, map_out); 82218edd6dSAnton Protopopov } 83218edd6dSAnton Protopopov 84218edd6dSAnton Protopopov /* 85218edd6dSAnton Protopopov * Load a program with two patches (get jiffies, for simplicity). Add an 86218edd6dSAnton Protopopov * insn_array map pointing to every instruction. Check how it was changed 87218edd6dSAnton Protopopov * after the program load. 88218edd6dSAnton Protopopov */ 89218edd6dSAnton Protopopov static void check_simple(void) 90218edd6dSAnton Protopopov { 91218edd6dSAnton Protopopov struct bpf_insn insns[] = { 92218edd6dSAnton Protopopov BPF_MOV64_IMM(BPF_REG_0, 2), 93218edd6dSAnton Protopopov BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_jiffies64), 94218edd6dSAnton Protopopov BPF_MOV64_IMM(BPF_REG_0, 1), 95218edd6dSAnton Protopopov BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_jiffies64), 96218edd6dSAnton Protopopov BPF_MOV64_IMM(BPF_REG_0, 0), 97218edd6dSAnton Protopopov BPF_EXIT_INSN(), 98218edd6dSAnton Protopopov }; 99218edd6dSAnton Protopopov __u32 map_in[] = {0, 1, 2, 3, 4, 5}; 100218edd6dSAnton Protopopov __u32 map_out[] = {0, 1, 4, 5, 8, 9}; 101218edd6dSAnton Protopopov 102218edd6dSAnton Protopopov __check_success(insns, ARRAY_SIZE(insns), map_in, map_out); 103218edd6dSAnton Protopopov } 104218edd6dSAnton Protopopov 105218edd6dSAnton Protopopov /* 106218edd6dSAnton Protopopov * Verifier can delete code in two cases: nops & dead code. From insn 107218edd6dSAnton Protopopov * array's point of view, the two cases are the same, so test using 108218edd6dSAnton Protopopov * the simplest method: by loading some nops 109218edd6dSAnton Protopopov */ 110218edd6dSAnton Protopopov static void check_deletions(void) 111218edd6dSAnton Protopopov { 112218edd6dSAnton Protopopov struct bpf_insn insns[] = { 113218edd6dSAnton Protopopov BPF_MOV64_IMM(BPF_REG_0, 2), 114218edd6dSAnton Protopopov BPF_JMP_IMM(BPF_JA, 0, 0, 0), /* nop */ 115218edd6dSAnton Protopopov BPF_MOV64_IMM(BPF_REG_0, 1), 116218edd6dSAnton Protopopov BPF_JMP_IMM(BPF_JA, 0, 0, 0), /* nop */ 117218edd6dSAnton Protopopov BPF_MOV64_IMM(BPF_REG_0, 0), 118218edd6dSAnton Protopopov BPF_EXIT_INSN(), 119218edd6dSAnton Protopopov }; 120218edd6dSAnton Protopopov __u32 map_in[] = {0, 1, 2, 3, 4, 5}; 121218edd6dSAnton Protopopov __u32 map_out[] = {0, -1, 1, -1, 2, 3}; 122218edd6dSAnton Protopopov 123218edd6dSAnton Protopopov __check_success(insns, ARRAY_SIZE(insns), map_in, map_out); 124218edd6dSAnton Protopopov } 125218edd6dSAnton Protopopov 126218edd6dSAnton Protopopov /* 127218edd6dSAnton Protopopov * Same test as check_deletions, but also add code which adds instructions 128218edd6dSAnton Protopopov */ 129218edd6dSAnton Protopopov static void check_deletions_with_functions(void) 130218edd6dSAnton Protopopov { 131218edd6dSAnton Protopopov struct bpf_insn insns[] = { 132218edd6dSAnton Protopopov BPF_JMP_IMM(BPF_JA, 0, 0, 0), /* nop */ 133218edd6dSAnton Protopopov BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_jiffies64), 134218edd6dSAnton Protopopov BPF_JMP_IMM(BPF_JA, 0, 0, 0), /* nop */ 135218edd6dSAnton Protopopov BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 2), 136218edd6dSAnton Protopopov BPF_MOV64_IMM(BPF_REG_0, 1), 137218edd6dSAnton Protopopov BPF_EXIT_INSN(), 138218edd6dSAnton Protopopov BPF_JMP_IMM(BPF_JA, 0, 0, 0), /* nop */ 139218edd6dSAnton Protopopov BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_jiffies64), 140218edd6dSAnton Protopopov BPF_JMP_IMM(BPF_JA, 0, 0, 0), /* nop */ 141218edd6dSAnton Protopopov BPF_MOV64_IMM(BPF_REG_0, 2), 142218edd6dSAnton Protopopov BPF_EXIT_INSN(), 143218edd6dSAnton Protopopov }; 144218edd6dSAnton Protopopov __u32 map_in[] = { 0, 1, 2, 3, 4, 5, /* func */ 6, 7, 8, 9, 10}; 145218edd6dSAnton Protopopov __u32 map_out[] = {-1, 0, -1, 3, 4, 5, /* func */ -1, 6, -1, 9, 10}; 146218edd6dSAnton Protopopov 147218edd6dSAnton Protopopov __check_success(insns, ARRAY_SIZE(insns), map_in, map_out); 148218edd6dSAnton Protopopov } 149218edd6dSAnton Protopopov 150218edd6dSAnton Protopopov /* 151218edd6dSAnton Protopopov * Try to load a program with a map which points to outside of the program 152218edd6dSAnton Protopopov */ 153218edd6dSAnton Protopopov static void check_out_of_bounds_index(void) 154218edd6dSAnton Protopopov { 155218edd6dSAnton Protopopov struct bpf_insn insns[] = { 156218edd6dSAnton Protopopov BPF_MOV64_IMM(BPF_REG_0, 4), 157218edd6dSAnton Protopopov BPF_MOV64_IMM(BPF_REG_0, 3), 158218edd6dSAnton Protopopov BPF_MOV64_IMM(BPF_REG_0, 2), 159218edd6dSAnton Protopopov BPF_MOV64_IMM(BPF_REG_0, 1), 160218edd6dSAnton Protopopov BPF_MOV64_IMM(BPF_REG_0, 0), 161218edd6dSAnton Protopopov BPF_EXIT_INSN(), 162218edd6dSAnton Protopopov }; 163218edd6dSAnton Protopopov int prog_fd, map_fd; 164218edd6dSAnton Protopopov struct bpf_insn_array_value val = {}; 165218edd6dSAnton Protopopov int key; 166218edd6dSAnton Protopopov 167218edd6dSAnton Protopopov map_fd = map_create(BPF_MAP_TYPE_INSN_ARRAY, 1); 168218edd6dSAnton Protopopov if (!ASSERT_GE(map_fd, 0, "map_create")) 169218edd6dSAnton Protopopov return; 170218edd6dSAnton Protopopov 171218edd6dSAnton Protopopov key = 0; 172218edd6dSAnton Protopopov val.orig_off = ARRAY_SIZE(insns); /* too big */ 173218edd6dSAnton Protopopov if (!ASSERT_EQ(bpf_map_update_elem(map_fd, &key, &val, 0), 0, "bpf_map_update_elem")) 174218edd6dSAnton Protopopov goto cleanup; 175218edd6dSAnton Protopopov 176218edd6dSAnton Protopopov if (!ASSERT_EQ(bpf_map_freeze(map_fd), 0, "bpf_map_freeze")) 177218edd6dSAnton Protopopov goto cleanup; 178218edd6dSAnton Protopopov 179218edd6dSAnton Protopopov prog_fd = prog_load(insns, ARRAY_SIZE(insns), &map_fd, 1); 180218edd6dSAnton Protopopov if (!ASSERT_EQ(prog_fd, -EINVAL, "program should have been rejected (prog_fd != -EINVAL)")) { 181218edd6dSAnton Protopopov close(prog_fd); 182218edd6dSAnton Protopopov goto cleanup; 183218edd6dSAnton Protopopov } 184218edd6dSAnton Protopopov 185218edd6dSAnton Protopopov cleanup: 186218edd6dSAnton Protopopov close(map_fd); 187218edd6dSAnton Protopopov } 188218edd6dSAnton Protopopov 189218edd6dSAnton Protopopov /* 190218edd6dSAnton Protopopov * Try to load a program with a map which points to the middle of 16-bit insn 191218edd6dSAnton Protopopov */ 192218edd6dSAnton Protopopov static void check_mid_insn_index(void) 193218edd6dSAnton Protopopov { 194218edd6dSAnton Protopopov struct bpf_insn insns[] = { 195218edd6dSAnton Protopopov BPF_LD_IMM64(BPF_REG_0, 0), /* 2 x 8 */ 196218edd6dSAnton Protopopov BPF_EXIT_INSN(), 197218edd6dSAnton Protopopov }; 198218edd6dSAnton Protopopov int prog_fd, map_fd; 199218edd6dSAnton Protopopov struct bpf_insn_array_value val = {}; 200218edd6dSAnton Protopopov int key; 201218edd6dSAnton Protopopov 202218edd6dSAnton Protopopov map_fd = map_create(BPF_MAP_TYPE_INSN_ARRAY, 1); 203218edd6dSAnton Protopopov if (!ASSERT_GE(map_fd, 0, "map_create")) 204218edd6dSAnton Protopopov return; 205218edd6dSAnton Protopopov 206218edd6dSAnton Protopopov key = 0; 207218edd6dSAnton Protopopov val.orig_off = 1; /* middle of 16-byte instruction */ 208218edd6dSAnton Protopopov if (!ASSERT_EQ(bpf_map_update_elem(map_fd, &key, &val, 0), 0, "bpf_map_update_elem")) 209218edd6dSAnton Protopopov goto cleanup; 210218edd6dSAnton Protopopov 211218edd6dSAnton Protopopov if (!ASSERT_EQ(bpf_map_freeze(map_fd), 0, "bpf_map_freeze")) 212218edd6dSAnton Protopopov goto cleanup; 213218edd6dSAnton Protopopov 214218edd6dSAnton Protopopov prog_fd = prog_load(insns, ARRAY_SIZE(insns), &map_fd, 1); 215218edd6dSAnton Protopopov if (!ASSERT_EQ(prog_fd, -EINVAL, "program should have been rejected (prog_fd != -EINVAL)")) { 216218edd6dSAnton Protopopov close(prog_fd); 217218edd6dSAnton Protopopov goto cleanup; 218218edd6dSAnton Protopopov } 219218edd6dSAnton Protopopov 220218edd6dSAnton Protopopov cleanup: 221218edd6dSAnton Protopopov close(map_fd); 222218edd6dSAnton Protopopov } 223218edd6dSAnton Protopopov 224218edd6dSAnton Protopopov static void check_incorrect_index(void) 225218edd6dSAnton Protopopov { 226218edd6dSAnton Protopopov check_out_of_bounds_index(); 227218edd6dSAnton Protopopov check_mid_insn_index(); 228218edd6dSAnton Protopopov } 229218edd6dSAnton Protopopov 230*ae48162aSAnton Protopopov static int set_bpf_jit_harden(char *level) 231*ae48162aSAnton Protopopov { 232*ae48162aSAnton Protopopov char old_level; 233*ae48162aSAnton Protopopov int err = -1; 234*ae48162aSAnton Protopopov int fd = -1; 235*ae48162aSAnton Protopopov 236*ae48162aSAnton Protopopov fd = open("/proc/sys/net/core/bpf_jit_harden", O_RDWR | O_NONBLOCK); 237*ae48162aSAnton Protopopov if (fd < 0) { 238*ae48162aSAnton Protopopov ASSERT_FAIL("open .../bpf_jit_harden returned %d (errno=%d)", fd, errno); 239*ae48162aSAnton Protopopov return -1; 240*ae48162aSAnton Protopopov } 241*ae48162aSAnton Protopopov 242*ae48162aSAnton Protopopov err = read(fd, &old_level, 1); 243*ae48162aSAnton Protopopov if (err != 1) { 244*ae48162aSAnton Protopopov ASSERT_FAIL("read from .../bpf_jit_harden returned %d (errno=%d)", err, errno); 245*ae48162aSAnton Protopopov err = -1; 246*ae48162aSAnton Protopopov goto end; 247*ae48162aSAnton Protopopov } 248*ae48162aSAnton Protopopov 249*ae48162aSAnton Protopopov lseek(fd, 0, SEEK_SET); 250*ae48162aSAnton Protopopov 251*ae48162aSAnton Protopopov err = write(fd, level, 1); 252*ae48162aSAnton Protopopov if (err != 1) { 253*ae48162aSAnton Protopopov ASSERT_FAIL("write to .../bpf_jit_harden returned %d (errno=%d)", err, errno); 254*ae48162aSAnton Protopopov err = -1; 255*ae48162aSAnton Protopopov goto end; 256*ae48162aSAnton Protopopov } 257*ae48162aSAnton Protopopov 258*ae48162aSAnton Protopopov err = 0; 259*ae48162aSAnton Protopopov *level = old_level; 260*ae48162aSAnton Protopopov end: 261*ae48162aSAnton Protopopov if (fd >= 0) 262*ae48162aSAnton Protopopov close(fd); 263*ae48162aSAnton Protopopov return err; 264*ae48162aSAnton Protopopov } 265*ae48162aSAnton Protopopov 266*ae48162aSAnton Protopopov static void check_blindness(void) 267*ae48162aSAnton Protopopov { 268*ae48162aSAnton Protopopov struct bpf_insn insns[] = { 269*ae48162aSAnton Protopopov BPF_MOV64_IMM(BPF_REG_0, 4), 270*ae48162aSAnton Protopopov BPF_MOV64_IMM(BPF_REG_0, 3), 271*ae48162aSAnton Protopopov BPF_MOV64_IMM(BPF_REG_0, 2), 272*ae48162aSAnton Protopopov BPF_MOV64_IMM(BPF_REG_0, 1), 273*ae48162aSAnton Protopopov BPF_EXIT_INSN(), 274*ae48162aSAnton Protopopov }; 275*ae48162aSAnton Protopopov int prog_fd = -1, map_fd; 276*ae48162aSAnton Protopopov struct bpf_insn_array_value val = {}; 277*ae48162aSAnton Protopopov char bpf_jit_harden = '@'; /* non-exizsting value */ 278*ae48162aSAnton Protopopov int i; 279*ae48162aSAnton Protopopov 280*ae48162aSAnton Protopopov map_fd = map_create(BPF_MAP_TYPE_INSN_ARRAY, ARRAY_SIZE(insns)); 281*ae48162aSAnton Protopopov if (!ASSERT_GE(map_fd, 0, "map_create")) 282*ae48162aSAnton Protopopov return; 283*ae48162aSAnton Protopopov 284*ae48162aSAnton Protopopov for (i = 0; i < ARRAY_SIZE(insns); i++) { 285*ae48162aSAnton Protopopov val.orig_off = i; 286*ae48162aSAnton Protopopov if (!ASSERT_EQ(bpf_map_update_elem(map_fd, &i, &val, 0), 0, "bpf_map_update_elem")) 287*ae48162aSAnton Protopopov goto cleanup; 288*ae48162aSAnton Protopopov } 289*ae48162aSAnton Protopopov 290*ae48162aSAnton Protopopov if (!ASSERT_EQ(bpf_map_freeze(map_fd), 0, "bpf_map_freeze")) 291*ae48162aSAnton Protopopov goto cleanup; 292*ae48162aSAnton Protopopov 293*ae48162aSAnton Protopopov bpf_jit_harden = '2'; 294*ae48162aSAnton Protopopov if (set_bpf_jit_harden(&bpf_jit_harden)) { 295*ae48162aSAnton Protopopov bpf_jit_harden = '@'; /* open, read or write failed => no write was done */ 296*ae48162aSAnton Protopopov goto cleanup; 297*ae48162aSAnton Protopopov } 298*ae48162aSAnton Protopopov 299*ae48162aSAnton Protopopov prog_fd = prog_load(insns, ARRAY_SIZE(insns), &map_fd, 1); 300*ae48162aSAnton Protopopov if (!ASSERT_GE(prog_fd, 0, "bpf(BPF_PROG_LOAD)")) 301*ae48162aSAnton Protopopov goto cleanup; 302*ae48162aSAnton Protopopov 303*ae48162aSAnton Protopopov for (i = 0; i < ARRAY_SIZE(insns); i++) { 304*ae48162aSAnton Protopopov char fmt[32]; 305*ae48162aSAnton Protopopov 306*ae48162aSAnton Protopopov if (!ASSERT_EQ(bpf_map_lookup_elem(map_fd, &i, &val), 0, "bpf_map_lookup_elem")) 307*ae48162aSAnton Protopopov goto cleanup; 308*ae48162aSAnton Protopopov 309*ae48162aSAnton Protopopov snprintf(fmt, sizeof(fmt), "val should be equal 3*%d", i); 310*ae48162aSAnton Protopopov ASSERT_EQ(val.xlated_off, i * 3, fmt); 311*ae48162aSAnton Protopopov } 312*ae48162aSAnton Protopopov 313*ae48162aSAnton Protopopov cleanup: 314*ae48162aSAnton Protopopov /* restore the old one */ 315*ae48162aSAnton Protopopov if (bpf_jit_harden != '@') 316*ae48162aSAnton Protopopov set_bpf_jit_harden(&bpf_jit_harden); 317*ae48162aSAnton Protopopov 318*ae48162aSAnton Protopopov close(prog_fd); 319*ae48162aSAnton Protopopov close(map_fd); 320*ae48162aSAnton Protopopov } 321*ae48162aSAnton Protopopov 322218edd6dSAnton Protopopov /* Once map was initialized, it should be frozen */ 323218edd6dSAnton Protopopov static void check_load_unfrozen_map(void) 324218edd6dSAnton Protopopov { 325218edd6dSAnton Protopopov struct bpf_insn insns[] = { 326218edd6dSAnton Protopopov BPF_MOV64_IMM(BPF_REG_0, 0), 327218edd6dSAnton Protopopov BPF_EXIT_INSN(), 328218edd6dSAnton Protopopov }; 329218edd6dSAnton Protopopov int prog_fd = -1, map_fd; 330218edd6dSAnton Protopopov struct bpf_insn_array_value val = {}; 331218edd6dSAnton Protopopov int i; 332218edd6dSAnton Protopopov 333218edd6dSAnton Protopopov map_fd = map_create(BPF_MAP_TYPE_INSN_ARRAY, ARRAY_SIZE(insns)); 334218edd6dSAnton Protopopov if (!ASSERT_GE(map_fd, 0, "map_create")) 335218edd6dSAnton Protopopov return; 336218edd6dSAnton Protopopov 337218edd6dSAnton Protopopov for (i = 0; i < ARRAY_SIZE(insns); i++) { 338218edd6dSAnton Protopopov val.orig_off = i; 339218edd6dSAnton Protopopov if (!ASSERT_EQ(bpf_map_update_elem(map_fd, &i, &val, 0), 0, "bpf_map_update_elem")) 340218edd6dSAnton Protopopov goto cleanup; 341218edd6dSAnton Protopopov } 342218edd6dSAnton Protopopov 343218edd6dSAnton Protopopov prog_fd = prog_load(insns, ARRAY_SIZE(insns), &map_fd, 1); 344218edd6dSAnton Protopopov if (!ASSERT_EQ(prog_fd, -EINVAL, "program should have been rejected (prog_fd != -EINVAL)")) 345218edd6dSAnton Protopopov goto cleanup; 346218edd6dSAnton Protopopov 347218edd6dSAnton Protopopov /* correctness: now freeze the map, the program should load fine */ 348218edd6dSAnton Protopopov 349218edd6dSAnton Protopopov if (!ASSERT_EQ(bpf_map_freeze(map_fd), 0, "bpf_map_freeze")) 350218edd6dSAnton Protopopov goto cleanup; 351218edd6dSAnton Protopopov 352218edd6dSAnton Protopopov prog_fd = prog_load(insns, ARRAY_SIZE(insns), &map_fd, 1); 353218edd6dSAnton Protopopov if (!ASSERT_GE(prog_fd, 0, "bpf(BPF_PROG_LOAD)")) 354218edd6dSAnton Protopopov goto cleanup; 355218edd6dSAnton Protopopov 356218edd6dSAnton Protopopov for (i = 0; i < ARRAY_SIZE(insns); i++) { 357218edd6dSAnton Protopopov if (!ASSERT_EQ(bpf_map_lookup_elem(map_fd, &i, &val), 0, "bpf_map_lookup_elem")) 358218edd6dSAnton Protopopov goto cleanup; 359218edd6dSAnton Protopopov 360218edd6dSAnton Protopopov ASSERT_EQ(val.xlated_off, i, "val should be equal i"); 361218edd6dSAnton Protopopov } 362218edd6dSAnton Protopopov 363218edd6dSAnton Protopopov cleanup: 364218edd6dSAnton Protopopov close(prog_fd); 365218edd6dSAnton Protopopov close(map_fd); 366218edd6dSAnton Protopopov } 367218edd6dSAnton Protopopov 368218edd6dSAnton Protopopov /* Map can be used only by one BPF program */ 369218edd6dSAnton Protopopov static void check_no_map_reuse(void) 370218edd6dSAnton Protopopov { 371218edd6dSAnton Protopopov struct bpf_insn insns[] = { 372218edd6dSAnton Protopopov BPF_MOV64_IMM(BPF_REG_0, 0), 373218edd6dSAnton Protopopov BPF_EXIT_INSN(), 374218edd6dSAnton Protopopov }; 375218edd6dSAnton Protopopov int prog_fd = -1, map_fd, extra_fd = -1; 376218edd6dSAnton Protopopov struct bpf_insn_array_value val = {}; 377218edd6dSAnton Protopopov int i; 378218edd6dSAnton Protopopov 379218edd6dSAnton Protopopov map_fd = map_create(BPF_MAP_TYPE_INSN_ARRAY, ARRAY_SIZE(insns)); 380218edd6dSAnton Protopopov if (!ASSERT_GE(map_fd, 0, "map_create")) 381218edd6dSAnton Protopopov return; 382218edd6dSAnton Protopopov 383218edd6dSAnton Protopopov for (i = 0; i < ARRAY_SIZE(insns); i++) { 384218edd6dSAnton Protopopov val.orig_off = i; 385218edd6dSAnton Protopopov if (!ASSERT_EQ(bpf_map_update_elem(map_fd, &i, &val, 0), 0, "bpf_map_update_elem")) 386218edd6dSAnton Protopopov goto cleanup; 387218edd6dSAnton Protopopov } 388218edd6dSAnton Protopopov 389218edd6dSAnton Protopopov if (!ASSERT_EQ(bpf_map_freeze(map_fd), 0, "bpf_map_freeze")) 390218edd6dSAnton Protopopov goto cleanup; 391218edd6dSAnton Protopopov 392218edd6dSAnton Protopopov prog_fd = prog_load(insns, ARRAY_SIZE(insns), &map_fd, 1); 393218edd6dSAnton Protopopov if (!ASSERT_GE(prog_fd, 0, "bpf(BPF_PROG_LOAD)")) 394218edd6dSAnton Protopopov goto cleanup; 395218edd6dSAnton Protopopov 396218edd6dSAnton Protopopov for (i = 0; i < ARRAY_SIZE(insns); i++) { 397218edd6dSAnton Protopopov if (!ASSERT_EQ(bpf_map_lookup_elem(map_fd, &i, &val), 0, "bpf_map_lookup_elem")) 398218edd6dSAnton Protopopov goto cleanup; 399218edd6dSAnton Protopopov 400218edd6dSAnton Protopopov ASSERT_EQ(val.xlated_off, i, "val should be equal i"); 401218edd6dSAnton Protopopov } 402218edd6dSAnton Protopopov 403218edd6dSAnton Protopopov extra_fd = prog_load(insns, ARRAY_SIZE(insns), &map_fd, 1); 404218edd6dSAnton Protopopov if (!ASSERT_EQ(extra_fd, -EBUSY, "program should have been rejected (extra_fd != -EBUSY)")) 405218edd6dSAnton Protopopov goto cleanup; 406218edd6dSAnton Protopopov 407218edd6dSAnton Protopopov /* correctness: check that prog is still loadable without fd_array */ 408218edd6dSAnton Protopopov extra_fd = prog_load(insns, ARRAY_SIZE(insns), NULL, 0); 409218edd6dSAnton Protopopov if (!ASSERT_GE(extra_fd, 0, "bpf(BPF_PROG_LOAD): expected no error")) 410218edd6dSAnton Protopopov goto cleanup; 411218edd6dSAnton Protopopov 412218edd6dSAnton Protopopov cleanup: 413218edd6dSAnton Protopopov close(extra_fd); 414218edd6dSAnton Protopopov close(prog_fd); 415218edd6dSAnton Protopopov close(map_fd); 416218edd6dSAnton Protopopov } 417218edd6dSAnton Protopopov 418218edd6dSAnton Protopopov static void check_bpf_no_lookup(void) 419218edd6dSAnton Protopopov { 420218edd6dSAnton Protopopov struct bpf_insn insns[] = { 421218edd6dSAnton Protopopov BPF_LD_MAP_FD(BPF_REG_1, 0), 422218edd6dSAnton Protopopov BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), 423218edd6dSAnton Protopopov BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), 424218edd6dSAnton Protopopov BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), 425218edd6dSAnton Protopopov BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), 426218edd6dSAnton Protopopov BPF_EXIT_INSN(), 427218edd6dSAnton Protopopov }; 428218edd6dSAnton Protopopov int prog_fd = -1, map_fd; 429218edd6dSAnton Protopopov 430218edd6dSAnton Protopopov map_fd = map_create(BPF_MAP_TYPE_INSN_ARRAY, 1); 431218edd6dSAnton Protopopov if (!ASSERT_GE(map_fd, 0, "map_create")) 432218edd6dSAnton Protopopov return; 433218edd6dSAnton Protopopov 434218edd6dSAnton Protopopov insns[0].imm = map_fd; 435218edd6dSAnton Protopopov 436218edd6dSAnton Protopopov if (!ASSERT_EQ(bpf_map_freeze(map_fd), 0, "bpf_map_freeze")) 437218edd6dSAnton Protopopov goto cleanup; 438218edd6dSAnton Protopopov 439218edd6dSAnton Protopopov prog_fd = prog_load(insns, ARRAY_SIZE(insns), NULL, 0); 440218edd6dSAnton Protopopov if (!ASSERT_EQ(prog_fd, -EINVAL, "program should have been rejected (prog_fd != -EINVAL)")) 441218edd6dSAnton Protopopov goto cleanup; 442218edd6dSAnton Protopopov 443218edd6dSAnton Protopopov /* correctness: check that prog is still loadable with normal map */ 444218edd6dSAnton Protopopov close(map_fd); 445218edd6dSAnton Protopopov map_fd = map_create(BPF_MAP_TYPE_ARRAY, 1); 446218edd6dSAnton Protopopov insns[0].imm = map_fd; 447218edd6dSAnton Protopopov prog_fd = prog_load(insns, ARRAY_SIZE(insns), NULL, 0); 448218edd6dSAnton Protopopov if (!ASSERT_GE(prog_fd, 0, "bpf(BPF_PROG_LOAD)")) 449218edd6dSAnton Protopopov goto cleanup; 450218edd6dSAnton Protopopov 451218edd6dSAnton Protopopov cleanup: 452218edd6dSAnton Protopopov close(prog_fd); 453218edd6dSAnton Protopopov close(map_fd); 454218edd6dSAnton Protopopov } 455218edd6dSAnton Protopopov 456218edd6dSAnton Protopopov static void check_bpf_side(void) 457218edd6dSAnton Protopopov { 458218edd6dSAnton Protopopov check_bpf_no_lookup(); 459218edd6dSAnton Protopopov } 460218edd6dSAnton Protopopov 461218edd6dSAnton Protopopov static void __test_bpf_insn_array(void) 462218edd6dSAnton Protopopov { 463218edd6dSAnton Protopopov /* Test if offsets are adjusted properly */ 464218edd6dSAnton Protopopov 465218edd6dSAnton Protopopov if (test__start_subtest("one2one")) 466218edd6dSAnton Protopopov check_one_to_one_mapping(); 467218edd6dSAnton Protopopov 468218edd6dSAnton Protopopov if (test__start_subtest("simple")) 469218edd6dSAnton Protopopov check_simple(); 470218edd6dSAnton Protopopov 471218edd6dSAnton Protopopov if (test__start_subtest("deletions")) 472218edd6dSAnton Protopopov check_deletions(); 473218edd6dSAnton Protopopov 474218edd6dSAnton Protopopov if (test__start_subtest("deletions-with-functions")) 475218edd6dSAnton Protopopov check_deletions_with_functions(); 476218edd6dSAnton Protopopov 477*ae48162aSAnton Protopopov if (test__start_subtest("blindness")) 478*ae48162aSAnton Protopopov check_blindness(); 479*ae48162aSAnton Protopopov 480218edd6dSAnton Protopopov /* Check all kinds of operations and related restrictions */ 481218edd6dSAnton Protopopov 482218edd6dSAnton Protopopov if (test__start_subtest("incorrect-index")) 483218edd6dSAnton Protopopov check_incorrect_index(); 484218edd6dSAnton Protopopov 485218edd6dSAnton Protopopov if (test__start_subtest("load-unfrozen-map")) 486218edd6dSAnton Protopopov check_load_unfrozen_map(); 487218edd6dSAnton Protopopov 488218edd6dSAnton Protopopov if (test__start_subtest("no-map-reuse")) 489218edd6dSAnton Protopopov check_no_map_reuse(); 490218edd6dSAnton Protopopov 491218edd6dSAnton Protopopov if (test__start_subtest("bpf-side-ops")) 492218edd6dSAnton Protopopov check_bpf_side(); 493218edd6dSAnton Protopopov } 494218edd6dSAnton Protopopov #else 495218edd6dSAnton Protopopov static void __test_bpf_insn_array(void) 496218edd6dSAnton Protopopov { 497218edd6dSAnton Protopopov test__skip(); 498218edd6dSAnton Protopopov } 499218edd6dSAnton Protopopov #endif 500218edd6dSAnton Protopopov 501218edd6dSAnton Protopopov void test_bpf_insn_array(void) 502218edd6dSAnton Protopopov { 503218edd6dSAnton Protopopov __test_bpf_insn_array(); 504218edd6dSAnton Protopopov } 505