aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--kernel/bpf/verifier.c48
-rw-r--r--tools/testing/selftests/bpf/test_verifier.c58
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
1620static 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, &reg_type); 1718 err = check_ctx_access(env, insn_idx, off, size, t, &reg_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
12263static int probe_filter_length(const struct bpf_insn *fp) 12319static int probe_filter_length(const struct bpf_insn *fp)