--- /dev/null
+From 58990d1ff3f7896ee341030e9a7c2e4002570683 Mon Sep 17 00:00:00 2001
+From: Daniel Borkmann <daniel@iogearbox.net>
+Date: Thu, 7 Jun 2018 17:40:03 +0200
+Subject: bpf: reject passing modified ctx to helper functions
+
+From: Daniel Borkmann <daniel@iogearbox.net>
+
+commit 58990d1ff3f7896ee341030e9a7c2e4002570683 upstream.
+
+As commit 28e33f9d78ee ("bpf: disallow arithmetic operations on
+context pointer") already describes, f1174f77b50c ("bpf/verifier:
+rework value tracking") removed the specific white-listed cases
+we had previously where we would allow for pointer arithmetic in
+order to further generalize it, and allow e.g. context access via
+modified registers. While the dereferencing of modified context
+pointers had been forbidden through 28e33f9d78ee, syzkaller did
+recently manage to trigger several KASAN splats for slab out of
+bounds access and use after frees by simply passing a modified
+context pointer to a helper function which would then do the bad
+access since verifier allowed it in adjust_ptr_min_max_vals().
+
+Rejecting arithmetic on ctx pointer in adjust_ptr_min_max_vals()
+generally could break existing programs as there's a valid use
+case in tracing in combination with passing the ctx to helpers as
+bpf_probe_read(), where the register then becomes unknown at
+verification time due to adding a non-constant offset to it. An
+access sequence may look like the following:
+
+ offset = args->filename; /* field __data_loc filename */
+ bpf_probe_read(&dst, len, (char *)args + offset); // args is ctx
+
+There are two options: i) we could special case the ctx and as
+soon as we add a constant or bounded offset to it (hence ctx type
+wouldn't change) we could turn the ctx into an unknown scalar, or
+ii) we generalize the sanity test for ctx member access into a
+small helper and assert it on the ctx register that was passed
+as a function argument. Fwiw, latter is more obvious and less
+complex at the same time, and one case that may potentially be
+legitimate in future for ctx member access at least would be for
+ctx to carry a const offset. Therefore, fix follows approach
+from ii) and adds test cases to BPF kselftests.
+
+Fixes: f1174f77b50c ("bpf/verifier: rework value tracking")
+Reported-by: syzbot+3d0b2441dbb71751615e@syzkaller.appspotmail.com
+Reported-by: syzbot+c8504affd4fdd0c1b626@syzkaller.appspotmail.com
+Reported-by: syzbot+e5190cb881d8660fb1a3@syzkaller.appspotmail.com
+Reported-by: syzbot+efae31b384d5badbd620@syzkaller.appspotmail.com
+Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
+Acked-by: Alexei Starovoitov <ast@kernel.org>
+Acked-by: Yonghong Song <yhs@fb.com>
+Acked-by: Edward Cree <ecree@solarflare.com>
+Signed-off-by: Alexei Starovoitov <ast@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ kernel/bpf/verifier.c | 48 ++++++++++++++---------
+ tools/testing/selftests/bpf/test_verifier.c | 58 +++++++++++++++++++++++++++-
+ 2 files changed, 88 insertions(+), 18 deletions(-)
+
+--- a/kernel/bpf/verifier.c
++++ b/kernel/bpf/verifier.c
+@@ -1610,6 +1610,30 @@ static int get_callee_stack_depth(struct
+ }
+ #endif
+
++static int check_ctx_reg(struct bpf_verifier_env *env,
++ const struct bpf_reg_state *reg, int regno)
++{
++ /* Access to ctx or passing it to a helper is only allowed in
++ * its original, unmodified form.
++ */
++
++ if (reg->off) {
++ verbose(env, "dereference of modified ctx ptr R%d off=%d disallowed\n",
++ regno, reg->off);
++ return -EACCES;
++ }
++
++ if (!tnum_is_const(reg->var_off) || reg->var_off.value) {
++ char tn_buf[48];
++
++ tnum_strn(tn_buf, sizeof(tn_buf), reg->var_off);
++ verbose(env, "variable ctx access var_off=%s disallowed\n", tn_buf);
++ return -EACCES;
++ }
++
++ return 0;
++}
++
+ /* truncate register to smaller size (in bytes)
+ * must be called with size < BPF_REG_SIZE
+ */
+@@ -1679,24 +1703,11 @@ static int check_mem_access(struct bpf_v
+ verbose(env, "R%d leaks addr into ctx\n", value_regno);
+ return -EACCES;
+ }
+- /* ctx accesses must be at a fixed offset, so that we can
+- * determine what type of data were returned.
+- */
+- if (reg->off) {
+- verbose(env,
+- "dereference of modified ctx ptr R%d off=%d+%d, ctx+const is allowed, ctx+const+const is not\n",
+- regno, reg->off, off - reg->off);
+- return -EACCES;
+- }
+- if (!tnum_is_const(reg->var_off) || reg->var_off.value) {
+- char tn_buf[48];
+
+- tnum_strn(tn_buf, sizeof(tn_buf), reg->var_off);
+- verbose(env,
+- "variable ctx access var_off=%s off=%d size=%d",
+- tn_buf, off, size);
+- return -EACCES;
+- }
++ err = check_ctx_reg(env, reg, regno);
++ if (err < 0)
++ return err;
++
+ err = check_ctx_access(env, insn_idx, off, size, t, ®_type);
+ if (!err && t == BPF_READ && value_regno >= 0) {
+ /* ctx access returns either a scalar, or a
+@@ -1977,6 +1988,9 @@ static int check_func_arg(struct bpf_ver
+ expected_type = PTR_TO_CTX;
+ if (type != expected_type)
+ goto err_type;
++ err = check_ctx_reg(env, reg, regno);
++ if (err < 0)
++ return err;
+ } else if (arg_type_is_mem_ptr(arg_type)) {
+ expected_type = PTR_TO_STACK;
+ /* One exception here. In case function allows for NULL to be
+--- a/tools/testing/selftests/bpf/test_verifier.c
++++ b/tools/testing/selftests/bpf/test_verifier.c
+@@ -8190,7 +8190,7 @@ static struct bpf_test tests[] = {
+ offsetof(struct __sk_buff, mark)),
+ BPF_EXIT_INSN(),
+ },
+- .errstr = "dereference of modified ctx ptr R1 off=68+8, ctx+const is allowed, ctx+const+const is not",
++ .errstr = "dereference of modified ctx ptr",
+ .result = REJECT,
+ .prog_type = BPF_PROG_TYPE_SCHED_CLS,
+ },
+@@ -11423,6 +11423,62 @@ static struct bpf_test tests[] = {
+ .errstr = "BPF_XADD stores into R2 packet",
+ .prog_type = BPF_PROG_TYPE_XDP,
+ },
++ {
++ "pass unmodified ctx pointer to helper",
++ .insns = {
++ BPF_MOV64_IMM(BPF_REG_2, 0),
++ BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
++ BPF_FUNC_csum_update),
++ BPF_MOV64_IMM(BPF_REG_0, 0),
++ BPF_EXIT_INSN(),
++ },
++ .prog_type = BPF_PROG_TYPE_SCHED_CLS,
++ .result = ACCEPT,
++ },
++ {
++ "pass modified ctx pointer to helper, 1",
++ .insns = {
++ BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -612),
++ BPF_MOV64_IMM(BPF_REG_2, 0),
++ BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
++ BPF_FUNC_csum_update),
++ BPF_MOV64_IMM(BPF_REG_0, 0),
++ BPF_EXIT_INSN(),
++ },
++ .prog_type = BPF_PROG_TYPE_SCHED_CLS,
++ .result = REJECT,
++ .errstr = "dereference of modified ctx ptr",
++ },
++ {
++ "pass modified ctx pointer to helper, 2",
++ .insns = {
++ BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -612),
++ BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
++ BPF_FUNC_get_socket_cookie),
++ BPF_MOV64_IMM(BPF_REG_0, 0),
++ BPF_EXIT_INSN(),
++ },
++ .result_unpriv = REJECT,
++ .result = REJECT,
++ .errstr_unpriv = "dereference of modified ctx ptr",
++ .errstr = "dereference of modified ctx ptr",
++ },
++ {
++ "pass modified ctx pointer to helper, 3",
++ .insns = {
++ BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, 0),
++ BPF_ALU64_IMM(BPF_AND, BPF_REG_3, 4),
++ BPF_ALU64_REG(BPF_ADD, BPF_REG_1, BPF_REG_3),
++ BPF_MOV64_IMM(BPF_REG_2, 0),
++ BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
++ BPF_FUNC_csum_update),
++ BPF_MOV64_IMM(BPF_REG_0, 0),
++ BPF_EXIT_INSN(),
++ },
++ .prog_type = BPF_PROG_TYPE_SCHED_CLS,
++ .result = REJECT,
++ .errstr = "variable ctx access var_off=(0x0; 0x4)",
++ },
+ };
+
+ static int probe_filter_length(const struct bpf_insn *fp)