diff options
Diffstat (limited to 'kernel/bpf')
| -rw-r--r-- | kernel/bpf/core.c | 4 | ||||
| -rw-r--r-- | kernel/bpf/disasm.c | 65 | ||||
| -rw-r--r-- | kernel/bpf/disasm.h | 29 | ||||
| -rw-r--r-- | kernel/bpf/syscall.c | 87 | ||||
| -rw-r--r-- | kernel/bpf/verifier.c | 30 |
5 files changed, 189 insertions, 26 deletions
diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c index 768e0a02d8c8..70a534549cd3 100644 --- a/kernel/bpf/core.c +++ b/kernel/bpf/core.c | |||
| @@ -771,7 +771,9 @@ struct bpf_prog *bpf_jit_blind_constants(struct bpf_prog *prog) | |||
| 771 | 771 | ||
| 772 | /* Base function for offset calculation. Needs to go into .text section, | 772 | /* Base function for offset calculation. Needs to go into .text section, |
| 773 | * therefore keeping it non-static as well; will also be used by JITs | 773 | * therefore keeping it non-static as well; will also be used by JITs |
| 774 | * anyway later on, so do not let the compiler omit it. | 774 | * anyway later on, so do not let the compiler omit it. This also needs |
| 775 | * to go into kallsyms for correlation from e.g. bpftool, so naming | ||
| 776 | * must not change. | ||
| 775 | */ | 777 | */ |
| 776 | noinline u64 __bpf_call_base(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5) | 778 | noinline u64 __bpf_call_base(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5) |
| 777 | { | 779 | { |
diff --git a/kernel/bpf/disasm.c b/kernel/bpf/disasm.c index 883f88fa5bfc..8740406df2cd 100644 --- a/kernel/bpf/disasm.c +++ b/kernel/bpf/disasm.c | |||
| @@ -21,10 +21,39 @@ static const char * const func_id_str[] = { | |||
| 21 | }; | 21 | }; |
| 22 | #undef __BPF_FUNC_STR_FN | 22 | #undef __BPF_FUNC_STR_FN |
| 23 | 23 | ||
| 24 | const char *func_id_name(int id) | 24 | static const char *__func_get_name(const struct bpf_insn_cbs *cbs, |
| 25 | const struct bpf_insn *insn, | ||
| 26 | char *buff, size_t len) | ||
| 25 | { | 27 | { |
| 26 | BUILD_BUG_ON(ARRAY_SIZE(func_id_str) != __BPF_FUNC_MAX_ID); | 28 | BUILD_BUG_ON(ARRAY_SIZE(func_id_str) != __BPF_FUNC_MAX_ID); |
| 27 | 29 | ||
| 30 | if (insn->src_reg != BPF_PSEUDO_CALL && | ||
| 31 | insn->imm >= 0 && insn->imm < __BPF_FUNC_MAX_ID && | ||
| 32 | func_id_str[insn->imm]) | ||
| 33 | return func_id_str[insn->imm]; | ||
| 34 | |||
| 35 | if (cbs && cbs->cb_call) | ||
| 36 | return cbs->cb_call(cbs->private_data, insn); | ||
| 37 | |||
| 38 | if (insn->src_reg == BPF_PSEUDO_CALL) | ||
| 39 | snprintf(buff, len, "%+d", insn->imm); | ||
| 40 | |||
| 41 | return buff; | ||
| 42 | } | ||
| 43 | |||
| 44 | static const char *__func_imm_name(const struct bpf_insn_cbs *cbs, | ||
| 45 | const struct bpf_insn *insn, | ||
| 46 | u64 full_imm, char *buff, size_t len) | ||
| 47 | { | ||
| 48 | if (cbs && cbs->cb_imm) | ||
| 49 | return cbs->cb_imm(cbs->private_data, insn, full_imm); | ||
| 50 | |||
| 51 | snprintf(buff, len, "0x%llx", (unsigned long long)full_imm); | ||
| 52 | return buff; | ||
| 53 | } | ||
| 54 | |||
| 55 | const char *func_id_name(int id) | ||
| 56 | { | ||
| 28 | if (id >= 0 && id < __BPF_FUNC_MAX_ID && func_id_str[id]) | 57 | if (id >= 0 && id < __BPF_FUNC_MAX_ID && func_id_str[id]) |
| 29 | return func_id_str[id]; | 58 | return func_id_str[id]; |
| 30 | else | 59 | else |
| @@ -83,7 +112,7 @@ static const char *const bpf_jmp_string[16] = { | |||
| 83 | [BPF_EXIT >> 4] = "exit", | 112 | [BPF_EXIT >> 4] = "exit", |
| 84 | }; | 113 | }; |
| 85 | 114 | ||
| 86 | static void print_bpf_end_insn(bpf_insn_print_cb verbose, | 115 | static void print_bpf_end_insn(bpf_insn_print_t verbose, |
| 87 | struct bpf_verifier_env *env, | 116 | struct bpf_verifier_env *env, |
| 88 | const struct bpf_insn *insn) | 117 | const struct bpf_insn *insn) |
| 89 | { | 118 | { |
| @@ -92,9 +121,12 @@ static void print_bpf_end_insn(bpf_insn_print_cb verbose, | |||
| 92 | insn->imm, insn->dst_reg); | 121 | insn->imm, insn->dst_reg); |
| 93 | } | 122 | } |
| 94 | 123 | ||
| 95 | void print_bpf_insn(bpf_insn_print_cb verbose, struct bpf_verifier_env *env, | 124 | void print_bpf_insn(const struct bpf_insn_cbs *cbs, |
| 96 | const struct bpf_insn *insn, bool allow_ptr_leaks) | 125 | struct bpf_verifier_env *env, |
| 126 | const struct bpf_insn *insn, | ||
| 127 | bool allow_ptr_leaks) | ||
| 97 | { | 128 | { |
| 129 | const bpf_insn_print_t verbose = cbs->cb_print; | ||
| 98 | u8 class = BPF_CLASS(insn->code); | 130 | u8 class = BPF_CLASS(insn->code); |
| 99 | 131 | ||
| 100 | if (class == BPF_ALU || class == BPF_ALU64) { | 132 | if (class == BPF_ALU || class == BPF_ALU64) { |
| @@ -175,12 +207,15 @@ void print_bpf_insn(bpf_insn_print_cb verbose, struct bpf_verifier_env *env, | |||
| 175 | */ | 207 | */ |
| 176 | u64 imm = ((u64)(insn + 1)->imm << 32) | (u32)insn->imm; | 208 | u64 imm = ((u64)(insn + 1)->imm << 32) | (u32)insn->imm; |
| 177 | bool map_ptr = insn->src_reg == BPF_PSEUDO_MAP_FD; | 209 | bool map_ptr = insn->src_reg == BPF_PSEUDO_MAP_FD; |
| 210 | char tmp[64]; | ||
| 178 | 211 | ||
| 179 | if (map_ptr && !allow_ptr_leaks) | 212 | if (map_ptr && !allow_ptr_leaks) |
| 180 | imm = 0; | 213 | imm = 0; |
| 181 | 214 | ||
| 182 | verbose(env, "(%02x) r%d = 0x%llx\n", insn->code, | 215 | verbose(env, "(%02x) r%d = %s\n", |
| 183 | insn->dst_reg, (unsigned long long)imm); | 216 | insn->code, insn->dst_reg, |
| 217 | __func_imm_name(cbs, insn, imm, | ||
| 218 | tmp, sizeof(tmp))); | ||
| 184 | } else { | 219 | } else { |
| 185 | verbose(env, "BUG_ld_%02x\n", insn->code); | 220 | verbose(env, "BUG_ld_%02x\n", insn->code); |
| 186 | return; | 221 | return; |
| @@ -189,12 +224,20 @@ void print_bpf_insn(bpf_insn_print_cb verbose, struct bpf_verifier_env *env, | |||
| 189 | u8 opcode = BPF_OP(insn->code); | 224 | u8 opcode = BPF_OP(insn->code); |
| 190 | 225 | ||
| 191 | if (opcode == BPF_CALL) { | 226 | if (opcode == BPF_CALL) { |
| 192 | if (insn->src_reg == BPF_PSEUDO_CALL) | 227 | char tmp[64]; |
| 193 | verbose(env, "(%02x) call pc%+d\n", insn->code, | 228 | |
| 194 | insn->imm); | 229 | if (insn->src_reg == BPF_PSEUDO_CALL) { |
| 195 | else | 230 | verbose(env, "(%02x) call pc%s\n", |
| 231 | insn->code, | ||
| 232 | __func_get_name(cbs, insn, | ||
| 233 | tmp, sizeof(tmp))); | ||
| 234 | } else { | ||
| 235 | strcpy(tmp, "unknown"); | ||
| 196 | verbose(env, "(%02x) call %s#%d\n", insn->code, | 236 | verbose(env, "(%02x) call %s#%d\n", insn->code, |
| 197 | func_id_name(insn->imm), insn->imm); | 237 | __func_get_name(cbs, insn, |
| 238 | tmp, sizeof(tmp)), | ||
| 239 | insn->imm); | ||
| 240 | } | ||
| 198 | } else if (insn->code == (BPF_JMP | BPF_JA)) { | 241 | } else if (insn->code == (BPF_JMP | BPF_JA)) { |
| 199 | verbose(env, "(%02x) goto pc%+d\n", | 242 | verbose(env, "(%02x) goto pc%+d\n", |
| 200 | insn->code, insn->off); | 243 | insn->code, insn->off); |
diff --git a/kernel/bpf/disasm.h b/kernel/bpf/disasm.h index 8de977e420b6..e0857d016f89 100644 --- a/kernel/bpf/disasm.h +++ b/kernel/bpf/disasm.h | |||
| @@ -17,16 +17,35 @@ | |||
| 17 | #include <linux/bpf.h> | 17 | #include <linux/bpf.h> |
| 18 | #include <linux/kernel.h> | 18 | #include <linux/kernel.h> |
| 19 | #include <linux/stringify.h> | 19 | #include <linux/stringify.h> |
| 20 | #ifndef __KERNEL__ | ||
| 21 | #include <stdio.h> | ||
| 22 | #include <string.h> | ||
| 23 | #endif | ||
| 24 | |||
| 25 | struct bpf_verifier_env; | ||
| 20 | 26 | ||
| 21 | extern const char *const bpf_alu_string[16]; | 27 | extern const char *const bpf_alu_string[16]; |
| 22 | extern const char *const bpf_class_string[8]; | 28 | extern const char *const bpf_class_string[8]; |
| 23 | 29 | ||
| 24 | const char *func_id_name(int id); | 30 | const char *func_id_name(int id); |
| 25 | 31 | ||
| 26 | struct bpf_verifier_env; | 32 | typedef void (*bpf_insn_print_t)(struct bpf_verifier_env *env, |
| 27 | typedef void (*bpf_insn_print_cb)(struct bpf_verifier_env *env, | 33 | const char *, ...); |
| 28 | const char *, ...); | 34 | typedef const char *(*bpf_insn_revmap_call_t)(void *private_data, |
| 29 | void print_bpf_insn(bpf_insn_print_cb verbose, struct bpf_verifier_env *env, | 35 | const struct bpf_insn *insn); |
| 30 | const struct bpf_insn *insn, bool allow_ptr_leaks); | 36 | typedef const char *(*bpf_insn_print_imm_t)(void *private_data, |
| 37 | const struct bpf_insn *insn, | ||
| 38 | __u64 full_imm); | ||
| 39 | |||
| 40 | struct bpf_insn_cbs { | ||
| 41 | bpf_insn_print_t cb_print; | ||
| 42 | bpf_insn_revmap_call_t cb_call; | ||
| 43 | bpf_insn_print_imm_t cb_imm; | ||
| 44 | void *private_data; | ||
| 45 | }; | ||
| 31 | 46 | ||
| 47 | void print_bpf_insn(const struct bpf_insn_cbs *cbs, | ||
| 48 | struct bpf_verifier_env *env, | ||
| 49 | const struct bpf_insn *insn, | ||
| 50 | bool allow_ptr_leaks); | ||
| 32 | #endif | 51 | #endif |
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 30e728dcd35d..007802c5ca7d 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c | |||
| @@ -1558,6 +1558,67 @@ static int bpf_map_get_fd_by_id(const union bpf_attr *attr) | |||
| 1558 | return fd; | 1558 | return fd; |
| 1559 | } | 1559 | } |
| 1560 | 1560 | ||
| 1561 | static const struct bpf_map *bpf_map_from_imm(const struct bpf_prog *prog, | ||
| 1562 | unsigned long addr) | ||
| 1563 | { | ||
| 1564 | int i; | ||
| 1565 | |||
| 1566 | for (i = 0; i < prog->aux->used_map_cnt; i++) | ||
| 1567 | if (prog->aux->used_maps[i] == (void *)addr) | ||
| 1568 | return prog->aux->used_maps[i]; | ||
| 1569 | return NULL; | ||
| 1570 | } | ||
| 1571 | |||
| 1572 | static struct bpf_insn *bpf_insn_prepare_dump(const struct bpf_prog *prog) | ||
| 1573 | { | ||
| 1574 | const struct bpf_map *map; | ||
| 1575 | struct bpf_insn *insns; | ||
| 1576 | u64 imm; | ||
| 1577 | int i; | ||
| 1578 | |||
| 1579 | insns = kmemdup(prog->insnsi, bpf_prog_insn_size(prog), | ||
| 1580 | GFP_USER); | ||
| 1581 | if (!insns) | ||
| 1582 | return insns; | ||
| 1583 | |||
| 1584 | for (i = 0; i < prog->len; i++) { | ||
| 1585 | if (insns[i].code == (BPF_JMP | BPF_TAIL_CALL)) { | ||
| 1586 | insns[i].code = BPF_JMP | BPF_CALL; | ||
| 1587 | insns[i].imm = BPF_FUNC_tail_call; | ||
| 1588 | /* fall-through */ | ||
| 1589 | } | ||
| 1590 | if (insns[i].code == (BPF_JMP | BPF_CALL) || | ||
| 1591 | insns[i].code == (BPF_JMP | BPF_CALL_ARGS)) { | ||
| 1592 | if (insns[i].code == (BPF_JMP | BPF_CALL_ARGS)) | ||
| 1593 | insns[i].code = BPF_JMP | BPF_CALL; | ||
| 1594 | if (!bpf_dump_raw_ok()) | ||
| 1595 | insns[i].imm = 0; | ||
| 1596 | continue; | ||
| 1597 | } | ||
| 1598 | |||
| 1599 | if (insns[i].code != (BPF_LD | BPF_IMM | BPF_DW)) | ||
| 1600 | continue; | ||
| 1601 | |||
| 1602 | imm = ((u64)insns[i + 1].imm << 32) | (u32)insns[i].imm; | ||
| 1603 | map = bpf_map_from_imm(prog, imm); | ||
| 1604 | if (map) { | ||
| 1605 | insns[i].src_reg = BPF_PSEUDO_MAP_FD; | ||
| 1606 | insns[i].imm = map->id; | ||
| 1607 | insns[i + 1].imm = 0; | ||
| 1608 | continue; | ||
| 1609 | } | ||
| 1610 | |||
| 1611 | if (!bpf_dump_raw_ok() && | ||
| 1612 | imm == (unsigned long)prog->aux) { | ||
| 1613 | insns[i].imm = 0; | ||
| 1614 | insns[i + 1].imm = 0; | ||
| 1615 | continue; | ||
| 1616 | } | ||
| 1617 | } | ||
| 1618 | |||
| 1619 | return insns; | ||
| 1620 | } | ||
| 1621 | |||
| 1561 | static int bpf_prog_get_info_by_fd(struct bpf_prog *prog, | 1622 | static int bpf_prog_get_info_by_fd(struct bpf_prog *prog, |
| 1562 | const union bpf_attr *attr, | 1623 | const union bpf_attr *attr, |
| 1563 | union bpf_attr __user *uattr) | 1624 | union bpf_attr __user *uattr) |
| @@ -1608,18 +1669,34 @@ static int bpf_prog_get_info_by_fd(struct bpf_prog *prog, | |||
| 1608 | ulen = info.jited_prog_len; | 1669 | ulen = info.jited_prog_len; |
| 1609 | info.jited_prog_len = prog->jited_len; | 1670 | info.jited_prog_len = prog->jited_len; |
| 1610 | if (info.jited_prog_len && ulen) { | 1671 | if (info.jited_prog_len && ulen) { |
| 1611 | uinsns = u64_to_user_ptr(info.jited_prog_insns); | 1672 | if (bpf_dump_raw_ok()) { |
| 1612 | ulen = min_t(u32, info.jited_prog_len, ulen); | 1673 | uinsns = u64_to_user_ptr(info.jited_prog_insns); |
| 1613 | if (copy_to_user(uinsns, prog->bpf_func, ulen)) | 1674 | ulen = min_t(u32, info.jited_prog_len, ulen); |
| 1614 | return -EFAULT; | 1675 | if (copy_to_user(uinsns, prog->bpf_func, ulen)) |
| 1676 | return -EFAULT; | ||
| 1677 | } else { | ||
| 1678 | info.jited_prog_insns = 0; | ||
| 1679 | } | ||
| 1615 | } | 1680 | } |
| 1616 | 1681 | ||
| 1617 | ulen = info.xlated_prog_len; | 1682 | ulen = info.xlated_prog_len; |
| 1618 | info.xlated_prog_len = bpf_prog_insn_size(prog); | 1683 | info.xlated_prog_len = bpf_prog_insn_size(prog); |
| 1619 | if (info.xlated_prog_len && ulen) { | 1684 | if (info.xlated_prog_len && ulen) { |
| 1685 | struct bpf_insn *insns_sanitized; | ||
| 1686 | bool fault; | ||
| 1687 | |||
| 1688 | if (prog->blinded && !bpf_dump_raw_ok()) { | ||
| 1689 | info.xlated_prog_insns = 0; | ||
| 1690 | goto done; | ||
| 1691 | } | ||
| 1692 | insns_sanitized = bpf_insn_prepare_dump(prog); | ||
| 1693 | if (!insns_sanitized) | ||
| 1694 | return -ENOMEM; | ||
| 1620 | uinsns = u64_to_user_ptr(info.xlated_prog_insns); | 1695 | uinsns = u64_to_user_ptr(info.xlated_prog_insns); |
| 1621 | ulen = min_t(u32, info.xlated_prog_len, ulen); | 1696 | ulen = min_t(u32, info.xlated_prog_len, ulen); |
| 1622 | if (copy_to_user(uinsns, prog->insnsi, ulen)) | 1697 | fault = copy_to_user(uinsns, insns_sanitized, ulen); |
| 1698 | kfree(insns_sanitized); | ||
| 1699 | if (fault) | ||
| 1623 | return -EFAULT; | 1700 | return -EFAULT; |
| 1624 | } | 1701 | } |
| 1625 | 1702 | ||
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 3c3eec58b3e8..4ae46b2cba88 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c | |||
| @@ -4427,9 +4427,12 @@ static int do_check(struct bpf_verifier_env *env) | |||
| 4427 | } | 4427 | } |
| 4428 | 4428 | ||
| 4429 | if (env->log.level) { | 4429 | if (env->log.level) { |
| 4430 | const struct bpf_insn_cbs cbs = { | ||
| 4431 | .cb_print = verbose, | ||
| 4432 | }; | ||
| 4433 | |||
| 4430 | verbose(env, "%d: ", insn_idx); | 4434 | verbose(env, "%d: ", insn_idx); |
| 4431 | print_bpf_insn(verbose, env, insn, | 4435 | print_bpf_insn(&cbs, env, insn, env->allow_ptr_leaks); |
| 4432 | env->allow_ptr_leaks); | ||
| 4433 | } | 4436 | } |
| 4434 | 4437 | ||
| 4435 | err = ext_analyzer_insn_hook(env, insn_idx, prev_insn_idx); | 4438 | err = ext_analyzer_insn_hook(env, insn_idx, prev_insn_idx); |
| @@ -5017,14 +5020,14 @@ static int jit_subprogs(struct bpf_verifier_env *env) | |||
| 5017 | { | 5020 | { |
| 5018 | struct bpf_prog *prog = env->prog, **func, *tmp; | 5021 | struct bpf_prog *prog = env->prog, **func, *tmp; |
| 5019 | int i, j, subprog_start, subprog_end = 0, len, subprog; | 5022 | int i, j, subprog_start, subprog_end = 0, len, subprog; |
| 5020 | struct bpf_insn *insn = prog->insnsi; | 5023 | struct bpf_insn *insn; |
| 5021 | void *old_bpf_func; | 5024 | void *old_bpf_func; |
| 5022 | int err = -ENOMEM; | 5025 | int err = -ENOMEM; |
| 5023 | 5026 | ||
| 5024 | if (env->subprog_cnt == 0) | 5027 | if (env->subprog_cnt == 0) |
| 5025 | return 0; | 5028 | return 0; |
| 5026 | 5029 | ||
| 5027 | for (i = 0; i < prog->len; i++, insn++) { | 5030 | for (i = 0, insn = prog->insnsi; i < prog->len; i++, insn++) { |
| 5028 | if (insn->code != (BPF_JMP | BPF_CALL) || | 5031 | if (insn->code != (BPF_JMP | BPF_CALL) || |
| 5029 | insn->src_reg != BPF_PSEUDO_CALL) | 5032 | insn->src_reg != BPF_PSEUDO_CALL) |
| 5030 | continue; | 5033 | continue; |
| @@ -5116,6 +5119,25 @@ static int jit_subprogs(struct bpf_verifier_env *env) | |||
| 5116 | bpf_prog_lock_ro(func[i]); | 5119 | bpf_prog_lock_ro(func[i]); |
| 5117 | bpf_prog_kallsyms_add(func[i]); | 5120 | bpf_prog_kallsyms_add(func[i]); |
| 5118 | } | 5121 | } |
| 5122 | |||
| 5123 | /* Last step: make now unused interpreter insns from main | ||
| 5124 | * prog consistent for later dump requests, so they can | ||
| 5125 | * later look the same as if they were interpreted only. | ||
| 5126 | */ | ||
| 5127 | for (i = 0, insn = prog->insnsi; i < prog->len; i++, insn++) { | ||
| 5128 | unsigned long addr; | ||
| 5129 | |||
| 5130 | if (insn->code != (BPF_JMP | BPF_CALL) || | ||
| 5131 | insn->src_reg != BPF_PSEUDO_CALL) | ||
| 5132 | continue; | ||
| 5133 | insn->off = env->insn_aux_data[i].call_imm; | ||
| 5134 | subprog = find_subprog(env, i + insn->off + 1); | ||
| 5135 | addr = (unsigned long)func[subprog + 1]->bpf_func; | ||
| 5136 | addr &= PAGE_MASK; | ||
| 5137 | insn->imm = (u64 (*)(u64, u64, u64, u64, u64)) | ||
| 5138 | addr - __bpf_call_base; | ||
| 5139 | } | ||
| 5140 | |||
| 5119 | prog->jited = 1; | 5141 | prog->jited = 1; |
| 5120 | prog->bpf_func = func[0]->bpf_func; | 5142 | prog->bpf_func = func[0]->bpf_func; |
| 5121 | prog->aux->func = func; | 5143 | prog->aux->func = func; |
