diff options
| -rw-r--r-- | kernel/bpf/verifier.c | 48 | ||||
| -rw-r--r-- | tools/testing/selftests/bpf/test_verifier.c | 58 |
2 files changed, 88 insertions, 18 deletions
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index d6403b5166f4..cced0c1e63e2 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c | |||
| @@ -1617,6 +1617,30 @@ static int get_callee_stack_depth(struct bpf_verifier_env *env, | |||
| 1617 | } | 1617 | } |
| 1618 | #endif | 1618 | #endif |
| 1619 | 1619 | ||
| 1620 | static int check_ctx_reg(struct bpf_verifier_env *env, | ||
| 1621 | const struct bpf_reg_state *reg, int regno) | ||
| 1622 | { | ||
| 1623 | /* Access to ctx or passing it to a helper is only allowed in | ||
| 1624 | * its original, unmodified form. | ||
| 1625 | */ | ||
| 1626 | |||
| 1627 | if (reg->off) { | ||
| 1628 | verbose(env, "dereference of modified ctx ptr R%d off=%d disallowed\n", | ||
| 1629 | regno, reg->off); | ||
| 1630 | return -EACCES; | ||
| 1631 | } | ||
| 1632 | |||
| 1633 | if (!tnum_is_const(reg->var_off) || reg->var_off.value) { | ||
| 1634 | char tn_buf[48]; | ||
| 1635 | |||
| 1636 | tnum_strn(tn_buf, sizeof(tn_buf), reg->var_off); | ||
| 1637 | verbose(env, "variable ctx access var_off=%s disallowed\n", tn_buf); | ||
| 1638 | return -EACCES; | ||
| 1639 | } | ||
| 1640 | |||
| 1641 | return 0; | ||
| 1642 | } | ||
| 1643 | |||
| 1620 | /* truncate register to smaller size (in bytes) | 1644 | /* truncate register to smaller size (in bytes) |
| 1621 | * must be called with size < BPF_REG_SIZE | 1645 | * must be called with size < BPF_REG_SIZE |
| 1622 | */ | 1646 | */ |
| @@ -1686,24 +1710,11 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn | |||
| 1686 | verbose(env, "R%d leaks addr into ctx\n", value_regno); | 1710 | verbose(env, "R%d leaks addr into ctx\n", value_regno); |
| 1687 | return -EACCES; | 1711 | return -EACCES; |
| 1688 | } | 1712 | } |
| 1689 | /* ctx accesses must be at a fixed offset, so that we can | ||
| 1690 | * determine what type of data were returned. | ||
| 1691 | */ | ||
| 1692 | if (reg->off) { | ||
| 1693 | verbose(env, | ||
| 1694 | "dereference of modified ctx ptr R%d off=%d+%d, ctx+const is allowed, ctx+const+const is not\n", | ||
| 1695 | regno, reg->off, off - reg->off); | ||
| 1696 | return -EACCES; | ||
| 1697 | } | ||
| 1698 | if (!tnum_is_const(reg->var_off) || reg->var_off.value) { | ||
| 1699 | char tn_buf[48]; | ||
| 1700 | 1713 | ||
| 1701 | tnum_strn(tn_buf, sizeof(tn_buf), reg->var_off); | 1714 | err = check_ctx_reg(env, reg, regno); |
| 1702 | verbose(env, | 1715 | if (err < 0) |
| 1703 | "variable ctx access var_off=%s off=%d size=%d", | 1716 | return err; |
| 1704 | tn_buf, off, size); | 1717 | |
| 1705 | return -EACCES; | ||
| 1706 | } | ||
| 1707 | err = check_ctx_access(env, insn_idx, off, size, t, ®_type); | 1718 | err = check_ctx_access(env, insn_idx, off, size, t, ®_type); |
| 1708 | if (!err && t == BPF_READ && value_regno >= 0) { | 1719 | if (!err && t == BPF_READ && value_regno >= 0) { |
| 1709 | /* ctx access returns either a scalar, or a | 1720 | /* ctx access returns either a scalar, or a |
| @@ -1984,6 +1995,9 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 regno, | |||
| 1984 | expected_type = PTR_TO_CTX; | 1995 | expected_type = PTR_TO_CTX; |
| 1985 | if (type != expected_type) | 1996 | if (type != expected_type) |
| 1986 | goto err_type; | 1997 | goto err_type; |
| 1998 | err = check_ctx_reg(env, reg, regno); | ||
| 1999 | if (err < 0) | ||
| 2000 | return err; | ||
| 1987 | } else if (arg_type_is_mem_ptr(arg_type)) { | 2001 | } else if (arg_type_is_mem_ptr(arg_type)) { |
| 1988 | expected_type = PTR_TO_STACK; | 2002 | expected_type = PTR_TO_STACK; |
| 1989 | /* One exception here. In case function allows for NULL to be | 2003 | /* One exception here. In case function allows for NULL to be |
diff --git a/tools/testing/selftests/bpf/test_verifier.c b/tools/testing/selftests/bpf/test_verifier.c index 7cb1d74057ce..2ecd27b670d7 100644 --- a/tools/testing/selftests/bpf/test_verifier.c +++ b/tools/testing/selftests/bpf/test_verifier.c | |||
| @@ -8647,7 +8647,7 @@ static struct bpf_test tests[] = { | |||
| 8647 | offsetof(struct __sk_buff, mark)), | 8647 | offsetof(struct __sk_buff, mark)), |
| 8648 | BPF_EXIT_INSN(), | 8648 | BPF_EXIT_INSN(), |
| 8649 | }, | 8649 | }, |
| 8650 | .errstr = "dereference of modified ctx ptr R1 off=68+8, ctx+const is allowed, ctx+const+const is not", | 8650 | .errstr = "dereference of modified ctx ptr", |
| 8651 | .result = REJECT, | 8651 | .result = REJECT, |
| 8652 | .prog_type = BPF_PROG_TYPE_SCHED_CLS, | 8652 | .prog_type = BPF_PROG_TYPE_SCHED_CLS, |
| 8653 | }, | 8653 | }, |
| @@ -12258,6 +12258,62 @@ static struct bpf_test tests[] = { | |||
| 12258 | .result = ACCEPT, | 12258 | .result = ACCEPT, |
| 12259 | .retval = 5, | 12259 | .retval = 5, |
| 12260 | }, | 12260 | }, |
| 12261 | { | ||
| 12262 | "pass unmodified ctx pointer to helper", | ||
| 12263 | .insns = { | ||
| 12264 | BPF_MOV64_IMM(BPF_REG_2, 0), | ||
| 12265 | BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, | ||
| 12266 | BPF_FUNC_csum_update), | ||
| 12267 | BPF_MOV64_IMM(BPF_REG_0, 0), | ||
| 12268 | BPF_EXIT_INSN(), | ||
| 12269 | }, | ||
| 12270 | .prog_type = BPF_PROG_TYPE_SCHED_CLS, | ||
| 12271 | .result = ACCEPT, | ||
| 12272 | }, | ||
| 12273 | { | ||
| 12274 | "pass modified ctx pointer to helper, 1", | ||
| 12275 | .insns = { | ||
| 12276 | BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -612), | ||
| 12277 | BPF_MOV64_IMM(BPF_REG_2, 0), | ||
| 12278 | BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, | ||
| 12279 | BPF_FUNC_csum_update), | ||
| 12280 | BPF_MOV64_IMM(BPF_REG_0, 0), | ||
| 12281 | BPF_EXIT_INSN(), | ||
| 12282 | }, | ||
| 12283 | .prog_type = BPF_PROG_TYPE_SCHED_CLS, | ||
| 12284 | .result = REJECT, | ||
| 12285 | .errstr = "dereference of modified ctx ptr", | ||
| 12286 | }, | ||
| 12287 | { | ||
| 12288 | "pass modified ctx pointer to helper, 2", | ||
| 12289 | .insns = { | ||
| 12290 | BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -612), | ||
| 12291 | BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, | ||
| 12292 | BPF_FUNC_get_socket_cookie), | ||
| 12293 | BPF_MOV64_IMM(BPF_REG_0, 0), | ||
| 12294 | BPF_EXIT_INSN(), | ||
| 12295 | }, | ||
| 12296 | .result_unpriv = REJECT, | ||
| 12297 | .result = REJECT, | ||
| 12298 | .errstr_unpriv = "dereference of modified ctx ptr", | ||
| 12299 | .errstr = "dereference of modified ctx ptr", | ||
| 12300 | }, | ||
| 12301 | { | ||
| 12302 | "pass modified ctx pointer to helper, 3", | ||
| 12303 | .insns = { | ||
| 12304 | BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, 0), | ||
| 12305 | BPF_ALU64_IMM(BPF_AND, BPF_REG_3, 4), | ||
| 12306 | BPF_ALU64_REG(BPF_ADD, BPF_REG_1, BPF_REG_3), | ||
| 12307 | BPF_MOV64_IMM(BPF_REG_2, 0), | ||
| 12308 | BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, | ||
| 12309 | BPF_FUNC_csum_update), | ||
| 12310 | BPF_MOV64_IMM(BPF_REG_0, 0), | ||
| 12311 | BPF_EXIT_INSN(), | ||
| 12312 | }, | ||
| 12313 | .prog_type = BPF_PROG_TYPE_SCHED_CLS, | ||
| 12314 | .result = REJECT, | ||
| 12315 | .errstr = "variable ctx access var_off=(0x0; 0x4)", | ||
| 12316 | }, | ||
| 12261 | }; | 12317 | }; |
| 12262 | 12318 | ||
| 12263 | static int probe_filter_length(const struct bpf_insn *fp) | 12319 | static int probe_filter_length(const struct bpf_insn *fp) |
