// SPDX-License-Identifier: GPL-2.0 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../../../include/linux/filter.h" #include "bpf_rlimit.h" static struct bpf_insn prog[BPF_MAXINSNS]; static void bpf_gen_imm_prog(unsigned int insns, int fd_map) { int i; srand(time(NULL)); for (i = 0; i < insns; i++) prog[i] = BPF_ALU64_IMM(BPF_MOV, i % BPF_REG_10, rand()); prog[i - 1] = BPF_EXIT_INSN(); } static void bpf_gen_map_prog(unsigned int insns, int fd_map) { int i, j = 0; for (i = 0; i + 1 < insns; i += 2) { struct bpf_insn tmp[] = { BPF_LD_MAP_FD(j++ % BPF_REG_10, fd_map) }; memcpy(&prog[i], tmp, sizeof(tmp)); } if (insns % 2 == 0) prog[insns - 2] = BPF_ALU64_IMM(BPF_MOV, i % BPF_REG_10, 42); prog[insns - 1] = BPF_EXIT_INSN(); } static int bpf_try_load_prog(int insns, int fd_map, void (*bpf_filler)(unsigned int insns, int fd_map)) { int fd_prog; bpf_filler(insns, fd_map); fd_prog = bpf_load_program(BPF_PROG_TYPE_SCHED_CLS, prog, insns, "", 0, NULL, 0); assert(fd_prog > 0); if (fd_map > 0) bpf_filler(insns, 0); return fd_prog; } static int __hex2bin(char ch) { if ((ch >= '0') && (ch <= '9')) return ch - '0'; ch = tolower(ch); if ((ch >= 'a') && (ch <= 'f')) return ch - 'a' + 10; return -1; } static int hex2bin(uint8_t *dst, const char *src, size_t count) { while (count--) { int hi = __hex2bin(*src++); int lo = __hex2bin(*src++); if ((hi < 0) || (lo < 0)) return -1; *dst++ = (hi << 4) | lo; } return 0; } static void tag_from_fdinfo(int fd_prog, uint8_t *tag, uint32_t len) { const int prefix_len = sizeof("prog_tag:\t") - 1; char buff[256]; int ret = -1; FILE *fp; snprintf(buff, sizeof(buff), "/proc/%d/fdinfo/%d", getpid(), fd_prog); fp = fopen(buff, "r"); assert(fp); while (fgets(buff, sizeof(buff), fp)) { if (strncmp(buff, "prog_tag:\t", prefix_len)) continue; ret = hex2bin(tag, buff + prefix_len, len); break; } fclose(fp); assert(!ret); } static void tag_from_alg(int insns, uint8_t *tag, uint32_t len) { static const struct sockaddr_alg alg = { .salg_family = AF_ALG, .salg_type = "hash", .salg_name = "sha1", }; int fd_base, fd_alg, ret; ssize_t size; fd_base = socket(AF_ALG, SOCK_SEQPACKET, 0); assert(fd_base > 0); ret = bind(fd_base, (struct sockaddr *)&alg, sizeof(alg)); assert(!ret); fd_alg = accept(fd_base, NULL, 0); assert(fd_alg > 0); insns *= sizeof(struct bpf_insn); size = write(fd_alg, prog, insns); assert(size == insns); size = read(fd_alg, tag, len); assert(size == len); close(fd_alg); close(fd_base); } static void tag_dump(const char *prefix, uint8_t *tag, uint32_t len) { int i; printf("%s", prefix); for (i = 0; i < len; i++) printf("%02x", tag[i]); printf("\n"); } static void tag_exit_report(int insns, int fd_map, uint8_t *ftag, uint8_t *atag, uint32_t len) { printf("Program tag mismatch for %d insns%s!\n", insns, fd_map < 0 ? "" : " with map"); tag_dump(" fdinfo result: ", ftag, len); tag_dump(" af_alg result: ", atag, len); exit(1); } static void do_test(uint32_t *tests, int start_insns, int fd_map, void (*bpf_filler)(unsigned int insns, int fd)) { int i, fd_prog; for (i = start_insns; i <= BPF_MAXINSNS; i++) { uint8_t ftag[8], atag[sizeof(ftag)]; fd_prog = bpf_try_load_prog(i, fd_map, bpf_filler); tag_from_fdinfo(fd_prog, ftag, sizeof(ftag)); tag_from_alg(i, atag, sizeof(atag)); if (memcmp(ftag, atag, sizeof(ftag))) tag_exit_report(i, fd_map, ftag, atag, sizeof(ftag)); close(fd_prog); sched_yield(); (*tests)++; } } int main(void) { uint32_t tests = 0; int i, fd_map; fd_map = bpf_create_map(BPF_MAP_TYPE_HASH, sizeof(int), sizeof(int), 1, BPF_F_NO_PREALLOC); assert(fd_map > 0); for (i = 0; i < 5; i++) { do_test(&tests, 2, -1, bpf_gen_imm_prog); do_test(&tests, 3, fd_map, bpf_gen_map_prog); } printf("test_tag: OK (%u tests)\n", tests); close(fd_map); return 0; }