Hi Greg,
please consider the following backported fixes for 4.9 inclusion.
Thanks a lot!
Alexei Starovoitov (2): bpf: adjust insn_aux_data when patching insns bpf: fix branch pruning logic
Jann Horn (2): bpf: reject out-of-bounds stack pointer calculation bpf: fix incorrect sign extension in check_alu_op()
include/linux/bpf_verifier.h | 1 + kernel/bpf/verifier.c | 106 +++++++++++++++++++++++++++++++++++++++---- 2 files changed, 97 insertions(+), 10 deletions(-)
From: Alexei Starovoitov ast@fb.com
[ Upstream commit 8041902dae5299c1f194ba42d14383f734631009 ]
convert_ctx_accesses() replaces single bpf instruction with a set of instructions. Adjust corresponding insn_aux_data while patching. It's needed to make sure subsequent 'for(all insn)' loops have matching insn and insn_aux_data.
Signed-off-by: Alexei Starovoitov ast@kernel.org Acked-by: Daniel Borkmann daniel@iogearbox.net Signed-off-by: David S. Miller davem@davemloft.net --- kernel/bpf/verifier.c | 44 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 39 insertions(+), 5 deletions(-)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 372454a..6faa5c1 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -3210,6 +3210,41 @@ static void convert_pseudo_ld_imm64(struct bpf_verifier_env *env) insn->src_reg = 0; }
+/* single env->prog->insni[off] instruction was replaced with the range + * insni[off, off + cnt). Adjust corresponding insn_aux_data by copying + * [0, off) and [off, end) to new locations, so the patched range stays zero + */ +static int adjust_insn_aux_data(struct bpf_verifier_env *env, u32 prog_len, + u32 off, u32 cnt) +{ + struct bpf_insn_aux_data *new_data, *old_data = env->insn_aux_data; + + if (cnt == 1) + return 0; + new_data = vzalloc(sizeof(struct bpf_insn_aux_data) * prog_len); + if (!new_data) + return -ENOMEM; + memcpy(new_data, old_data, sizeof(struct bpf_insn_aux_data) * off); + memcpy(new_data + off + cnt - 1, old_data + off, + sizeof(struct bpf_insn_aux_data) * (prog_len - off - cnt + 1)); + env->insn_aux_data = new_data; + vfree(old_data); + return 0; +} + +static struct bpf_prog *bpf_patch_insn_data(struct bpf_verifier_env *env, u32 off, + const struct bpf_insn *patch, u32 len) +{ + struct bpf_prog *new_prog; + + new_prog = bpf_patch_insn_single(env->prog, off, patch, len); + if (!new_prog) + return NULL; + if (adjust_insn_aux_data(env, new_prog->len, off, len)) + return NULL; + return new_prog; +} + /* convert load instructions that access fields of 'struct __sk_buff' * into sequence of instructions that access fields of 'struct sk_buff' */ @@ -3229,10 +3264,10 @@ static int convert_ctx_accesses(struct bpf_verifier_env *env) verbose("bpf verifier is misconfigured\n"); return -EINVAL; } else if (cnt) { - new_prog = bpf_patch_insn_single(env->prog, 0, - insn_buf, cnt); + new_prog = bpf_patch_insn_data(env, 0, insn_buf, cnt); if (!new_prog) return -ENOMEM; + env->prog = new_prog; delta += cnt - 1; } @@ -3253,7 +3288,7 @@ static int convert_ctx_accesses(struct bpf_verifier_env *env) else continue;
- if (env->insn_aux_data[i].ptr_type != PTR_TO_CTX) + if (env->insn_aux_data[i + delta].ptr_type != PTR_TO_CTX) continue;
cnt = ops->convert_ctx_access(type, insn->dst_reg, insn->src_reg, @@ -3263,8 +3298,7 @@ static int convert_ctx_accesses(struct bpf_verifier_env *env) return -EINVAL; }
- new_prog = bpf_patch_insn_single(env->prog, i + delta, insn_buf, - cnt); + new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt); if (!new_prog) return -ENOMEM;
This is a note to let you know that I've just added the patch titled
bpf: adjust insn_aux_data when patching insns
to the 4.9-stable tree which can be found at: http://www.kernel.org/git/?p=linux/kernel/git/stable/stable-queue.git%3Ba=su...
The filename of the patch is: bpf-adjust-insn_aux_data-when-patching-insns.patch and it can be found in the queue-4.9 subdirectory.
If you, or anyone else, feels it should not be added to the stable tree, please let stable@vger.kernel.org know about it.
From foo@baz Fri Dec 22 16:57:35 CET 2017
From: Daniel Borkmann daniel@iogearbox.net Date: Fri, 22 Dec 2017 16:29:02 +0100 Subject: bpf: adjust insn_aux_data when patching insns To: gregkh@linuxfoundation.org Cc: ast@kernel.org, daniel@iogearbox.net, jannh@google.com, stable@vger.kernel.org, Alexei Starovoitov ast@fb.com, "David S . Miller" davem@davemloft.net Message-ID: 20171222152905.3455-2-daniel@iogearbox.net
From: Daniel Borkmann daniel@iogearbox.net
From: Alexei Starovoitov ast@fb.com
[ Upstream commit 8041902dae5299c1f194ba42d14383f734631009 ]
convert_ctx_accesses() replaces single bpf instruction with a set of instructions. Adjust corresponding insn_aux_data while patching. It's needed to make sure subsequent 'for(all insn)' loops have matching insn and insn_aux_data.
Signed-off-by: Alexei Starovoitov ast@kernel.org Acked-by: Daniel Borkmann daniel@iogearbox.net Signed-off-by: David S. Miller davem@davemloft.net Signed-off-by: Greg Kroah-Hartman gregkh@linuxfoundation.org --- kernel/bpf/verifier.c | 44 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 39 insertions(+), 5 deletions(-)
--- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -3210,6 +3210,41 @@ static void convert_pseudo_ld_imm64(stru insn->src_reg = 0; }
+/* single env->prog->insni[off] instruction was replaced with the range + * insni[off, off + cnt). Adjust corresponding insn_aux_data by copying + * [0, off) and [off, end) to new locations, so the patched range stays zero + */ +static int adjust_insn_aux_data(struct bpf_verifier_env *env, u32 prog_len, + u32 off, u32 cnt) +{ + struct bpf_insn_aux_data *new_data, *old_data = env->insn_aux_data; + + if (cnt == 1) + return 0; + new_data = vzalloc(sizeof(struct bpf_insn_aux_data) * prog_len); + if (!new_data) + return -ENOMEM; + memcpy(new_data, old_data, sizeof(struct bpf_insn_aux_data) * off); + memcpy(new_data + off + cnt - 1, old_data + off, + sizeof(struct bpf_insn_aux_data) * (prog_len - off - cnt + 1)); + env->insn_aux_data = new_data; + vfree(old_data); + return 0; +} + +static struct bpf_prog *bpf_patch_insn_data(struct bpf_verifier_env *env, u32 off, + const struct bpf_insn *patch, u32 len) +{ + struct bpf_prog *new_prog; + + new_prog = bpf_patch_insn_single(env->prog, off, patch, len); + if (!new_prog) + return NULL; + if (adjust_insn_aux_data(env, new_prog->len, off, len)) + return NULL; + return new_prog; +} + /* convert load instructions that access fields of 'struct __sk_buff' * into sequence of instructions that access fields of 'struct sk_buff' */ @@ -3229,10 +3264,10 @@ static int convert_ctx_accesses(struct b verbose("bpf verifier is misconfigured\n"); return -EINVAL; } else if (cnt) { - new_prog = bpf_patch_insn_single(env->prog, 0, - insn_buf, cnt); + new_prog = bpf_patch_insn_data(env, 0, insn_buf, cnt); if (!new_prog) return -ENOMEM; + env->prog = new_prog; delta += cnt - 1; } @@ -3253,7 +3288,7 @@ static int convert_ctx_accesses(struct b else continue;
- if (env->insn_aux_data[i].ptr_type != PTR_TO_CTX) + if (env->insn_aux_data[i + delta].ptr_type != PTR_TO_CTX) continue;
cnt = ops->convert_ctx_access(type, insn->dst_reg, insn->src_reg, @@ -3263,8 +3298,7 @@ static int convert_ctx_accesses(struct b return -EINVAL; }
- new_prog = bpf_patch_insn_single(env->prog, i + delta, insn_buf, - cnt); + new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt); if (!new_prog) return -ENOMEM;
Patches currently in stable-queue which might be from daniel@iogearbox.net are
queue-4.9/bpf-fix-branch-pruning-logic.patch queue-4.9/bpf-adjust-insn_aux_data-when-patching-insns.patch queue-4.9/bpf-fix-incorrect-sign-extension-in-check_alu_op.patch queue-4.9/bpf-reject-out-of-bounds-stack-pointer-calculation.patch
From: Alexei Starovoitov ast@fb.com
[ Upstream commit c131187db2d3fa2f8bf32fdf4e9a4ef805168467 ]
when the verifier detects that register contains a runtime constant and it's compared with another constant it will prune exploration of the branch that is guaranteed not to be taken at runtime. This is all correct, but malicious program may be constructed in such a way that it always has a constant comparison and the other branch is never taken under any conditions. In this case such path through the program will not be explored by the verifier. It won't be taken at run-time either, but since all instructions are JITed the malicious program may cause JITs to complain about using reserved fields, etc. To fix the issue we have to track the instructions explored by the verifier and sanitize instructions that are dead at run time with NOPs. We cannot reject such dead code, since llvm generates it for valid C code, since it doesn't do as much data flow analysis as the verifier does.
Fixes: 17a5267067f3 ("bpf: verifier (add verifier core)") Signed-off-by: Alexei Starovoitov ast@kernel.org Acked-by: Daniel Borkmann daniel@iogearbox.net Signed-off-by: Daniel Borkmann daniel@iogearbox.net --- include/linux/bpf_verifier.h | 1 + kernel/bpf/verifier.c | 27 +++++++++++++++++++++++++++ 2 files changed, 28 insertions(+)
diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h index 3101141..4c4e935 100644 --- a/include/linux/bpf_verifier.h +++ b/include/linux/bpf_verifier.h @@ -68,6 +68,7 @@ struct bpf_verifier_state_list {
struct bpf_insn_aux_data { enum bpf_reg_type ptr_type; /* pointer type for load/store insns */ + bool seen; /* this insn was processed by the verifier */ };
#define MAX_USED_MAPS 64 /* max number of maps accessed by one eBPF program */ diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 6faa5c1..2f19c94 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -2862,6 +2862,7 @@ static int do_check(struct bpf_verifier_env *env) if (err) return err;
+ env->insn_aux_data[insn_idx].seen = true; if (class == BPF_ALU || class == BPF_ALU64) { err = check_alu_op(env, insn); if (err) @@ -3059,6 +3060,7 @@ static int do_check(struct bpf_verifier_env *env) return err;
insn_idx++; + env->insn_aux_data[insn_idx].seen = true; } else { verbose("invalid BPF_LD mode\n"); return -EINVAL; @@ -3218,6 +3220,7 @@ static int adjust_insn_aux_data(struct bpf_verifier_env *env, u32 prog_len, u32 off, u32 cnt) { struct bpf_insn_aux_data *new_data, *old_data = env->insn_aux_data; + int i;
if (cnt == 1) return 0; @@ -3227,6 +3230,8 @@ static int adjust_insn_aux_data(struct bpf_verifier_env *env, u32 prog_len, memcpy(new_data, old_data, sizeof(struct bpf_insn_aux_data) * off); memcpy(new_data + off + cnt - 1, old_data + off, sizeof(struct bpf_insn_aux_data) * (prog_len - off - cnt + 1)); + for (i = off; i < off + cnt - 1; i++) + new_data[i].seen = true; env->insn_aux_data = new_data; vfree(old_data); return 0; @@ -3245,6 +3250,25 @@ static struct bpf_prog *bpf_patch_insn_data(struct bpf_verifier_env *env, u32 of return new_prog; }
+/* The verifier does more data flow analysis than llvm and will not explore + * branches that are dead at run time. Malicious programs can have dead code + * too. Therefore replace all dead at-run-time code with nops. + */ +static void sanitize_dead_code(struct bpf_verifier_env *env) +{ + struct bpf_insn_aux_data *aux_data = env->insn_aux_data; + struct bpf_insn nop = BPF_MOV64_REG(BPF_REG_0, BPF_REG_0); + struct bpf_insn *insn = env->prog->insnsi; + const int insn_cnt = env->prog->len; + int i; + + for (i = 0; i < insn_cnt; i++) { + if (aux_data[i].seen) + continue; + memcpy(insn + i, &nop, sizeof(nop)); + } +} + /* convert load instructions that access fields of 'struct __sk_buff' * into sequence of instructions that access fields of 'struct sk_buff' */ @@ -3407,6 +3431,9 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr) free_states(env);
if (ret == 0) + sanitize_dead_code(env); + + if (ret == 0) /* program is valid, convert *(u32*)(ctx + off) accesses */ ret = convert_ctx_accesses(env);
This is a note to let you know that I've just added the patch titled
bpf: fix branch pruning logic
to the 4.9-stable tree which can be found at: http://www.kernel.org/git/?p=linux/kernel/git/stable/stable-queue.git%3Ba=su...
The filename of the patch is: bpf-fix-branch-pruning-logic.patch and it can be found in the queue-4.9 subdirectory.
If you, or anyone else, feels it should not be added to the stable tree, please let stable@vger.kernel.org know about it.
From foo@baz Fri Dec 22 16:57:35 CET 2017
From: Daniel Borkmann daniel@iogearbox.net Date: Fri, 22 Dec 2017 16:29:03 +0100 Subject: bpf: fix branch pruning logic To: gregkh@linuxfoundation.org Cc: ast@kernel.org, daniel@iogearbox.net, jannh@google.com, stable@vger.kernel.org, Alexei Starovoitov ast@fb.com Message-ID: 20171222152905.3455-3-daniel@iogearbox.net
From: Daniel Borkmann daniel@iogearbox.net
From: Alexei Starovoitov ast@fb.com
[ Upstream commit c131187db2d3fa2f8bf32fdf4e9a4ef805168467 ]
when the verifier detects that register contains a runtime constant and it's compared with another constant it will prune exploration of the branch that is guaranteed not to be taken at runtime. This is all correct, but malicious program may be constructed in such a way that it always has a constant comparison and the other branch is never taken under any conditions. In this case such path through the program will not be explored by the verifier. It won't be taken at run-time either, but since all instructions are JITed the malicious program may cause JITs to complain about using reserved fields, etc. To fix the issue we have to track the instructions explored by the verifier and sanitize instructions that are dead at run time with NOPs. We cannot reject such dead code, since llvm generates it for valid C code, since it doesn't do as much data flow analysis as the verifier does.
Fixes: 17a5267067f3 ("bpf: verifier (add verifier core)") Signed-off-by: Alexei Starovoitov ast@kernel.org Acked-by: Daniel Borkmann daniel@iogearbox.net Signed-off-by: Daniel Borkmann daniel@iogearbox.net Signed-off-by: Greg Kroah-Hartman gregkh@linuxfoundation.org --- include/linux/bpf_verifier.h | 1 + kernel/bpf/verifier.c | 27 +++++++++++++++++++++++++++ 2 files changed, 28 insertions(+)
--- a/include/linux/bpf_verifier.h +++ b/include/linux/bpf_verifier.h @@ -68,6 +68,7 @@ struct bpf_verifier_state_list {
struct bpf_insn_aux_data { enum bpf_reg_type ptr_type; /* pointer type for load/store insns */ + bool seen; /* this insn was processed by the verifier */ };
#define MAX_USED_MAPS 64 /* max number of maps accessed by one eBPF program */ --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -2862,6 +2862,7 @@ static int do_check(struct bpf_verifier_ if (err) return err;
+ env->insn_aux_data[insn_idx].seen = true; if (class == BPF_ALU || class == BPF_ALU64) { err = check_alu_op(env, insn); if (err) @@ -3059,6 +3060,7 @@ process_bpf_exit: return err;
insn_idx++; + env->insn_aux_data[insn_idx].seen = true; } else { verbose("invalid BPF_LD mode\n"); return -EINVAL; @@ -3218,6 +3220,7 @@ static int adjust_insn_aux_data(struct b u32 off, u32 cnt) { struct bpf_insn_aux_data *new_data, *old_data = env->insn_aux_data; + int i;
if (cnt == 1) return 0; @@ -3227,6 +3230,8 @@ static int adjust_insn_aux_data(struct b memcpy(new_data, old_data, sizeof(struct bpf_insn_aux_data) * off); memcpy(new_data + off + cnt - 1, old_data + off, sizeof(struct bpf_insn_aux_data) * (prog_len - off - cnt + 1)); + for (i = off; i < off + cnt - 1; i++) + new_data[i].seen = true; env->insn_aux_data = new_data; vfree(old_data); return 0; @@ -3245,6 +3250,25 @@ static struct bpf_prog *bpf_patch_insn_d return new_prog; }
+/* The verifier does more data flow analysis than llvm and will not explore + * branches that are dead at run time. Malicious programs can have dead code + * too. Therefore replace all dead at-run-time code with nops. + */ +static void sanitize_dead_code(struct bpf_verifier_env *env) +{ + struct bpf_insn_aux_data *aux_data = env->insn_aux_data; + struct bpf_insn nop = BPF_MOV64_REG(BPF_REG_0, BPF_REG_0); + struct bpf_insn *insn = env->prog->insnsi; + const int insn_cnt = env->prog->len; + int i; + + for (i = 0; i < insn_cnt; i++) { + if (aux_data[i].seen) + continue; + memcpy(insn + i, &nop, sizeof(nop)); + } +} + /* convert load instructions that access fields of 'struct __sk_buff' * into sequence of instructions that access fields of 'struct sk_buff' */ @@ -3407,6 +3431,9 @@ skip_full_check: free_states(env);
if (ret == 0) + sanitize_dead_code(env); + + if (ret == 0) /* program is valid, convert *(u32*)(ctx + off) accesses */ ret = convert_ctx_accesses(env);
Patches currently in stable-queue which might be from daniel@iogearbox.net are
queue-4.9/bpf-fix-branch-pruning-logic.patch queue-4.9/bpf-adjust-insn_aux_data-when-patching-insns.patch queue-4.9/bpf-fix-incorrect-sign-extension-in-check_alu_op.patch queue-4.9/bpf-reject-out-of-bounds-stack-pointer-calculation.patch
From: Jann Horn jannh@google.com
Reject programs that compute wildly out-of-bounds stack pointers. Otherwise, pointers can be computed with an offset that doesn't fit into an `int`, causing security issues in the stack memory access check (as well as signed integer overflow during offset addition).
This is a fix specifically for the v4.9 stable tree because the mainline code looks very different at this point.
Fixes: 7bca0a9702edf ("bpf: enhance verifier to understand stack pointer arithmetic") Signed-off-by: Jann Horn jannh@google.com Acked-by: Daniel Borkmann daniel@iogearbox.net --- kernel/bpf/verifier.c | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 2f19c94..b03af36 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -1861,10 +1861,28 @@ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn) ((BPF_SRC(insn->code) == BPF_X && regs[insn->src_reg].type == CONST_IMM) || BPF_SRC(insn->code) == BPF_K)) { - if (BPF_SRC(insn->code) == BPF_X) + if (BPF_SRC(insn->code) == BPF_X) { + /* check in case the register contains a big + * 64-bit value + */ + if (regs[insn->src_reg].imm < -MAX_BPF_STACK || + regs[insn->src_reg].imm > MAX_BPF_STACK) { + verbose("R%d value too big in R%d pointer arithmetic\n", + insn->src_reg, insn->dst_reg); + return -EACCES; + } dst_reg->imm += regs[insn->src_reg].imm; - else + } else { + /* safe against overflow: addition of 32-bit + * numbers in 64-bit representation + */ dst_reg->imm += insn->imm; + } + if (dst_reg->imm > 0 || dst_reg->imm < -MAX_BPF_STACK) { + verbose("R%d out-of-bounds pointer arithmetic\n", + insn->dst_reg); + return -EACCES; + } return 0; } else if (opcode == BPF_ADD && BPF_CLASS(insn->code) == BPF_ALU64 &&
This is a note to let you know that I've just added the patch titled
bpf: reject out-of-bounds stack pointer calculation
to the 4.9-stable tree which can be found at: http://www.kernel.org/git/?p=linux/kernel/git/stable/stable-queue.git%3Ba=su...
The filename of the patch is: bpf-reject-out-of-bounds-stack-pointer-calculation.patch and it can be found in the queue-4.9 subdirectory.
If you, or anyone else, feels it should not be added to the stable tree, please let stable@vger.kernel.org know about it.
From foo@baz Fri Dec 22 16:57:35 CET 2017
From: Daniel Borkmann daniel@iogearbox.net Date: Fri, 22 Dec 2017 16:29:04 +0100 Subject: bpf: reject out-of-bounds stack pointer calculation To: gregkh@linuxfoundation.org Cc: ast@kernel.org, daniel@iogearbox.net, jannh@google.com, stable@vger.kernel.org Message-ID: 20171222152905.3455-4-daniel@iogearbox.net
From: Daniel Borkmann daniel@iogearbox.net
From: Jann Horn jannh@google.com
Reject programs that compute wildly out-of-bounds stack pointers. Otherwise, pointers can be computed with an offset that doesn't fit into an `int`, causing security issues in the stack memory access check (as well as signed integer overflow during offset addition).
This is a fix specifically for the v4.9 stable tree because the mainline code looks very different at this point.
Fixes: 7bca0a9702edf ("bpf: enhance verifier to understand stack pointer arithmetic") Signed-off-by: Jann Horn jannh@google.com Acked-by: Daniel Borkmann daniel@iogearbox.net Signed-off-by: Greg Kroah-Hartman gregkh@linuxfoundation.org --- kernel/bpf/verifier.c | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-)
--- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -1861,10 +1861,28 @@ static int check_alu_op(struct bpf_verif ((BPF_SRC(insn->code) == BPF_X && regs[insn->src_reg].type == CONST_IMM) || BPF_SRC(insn->code) == BPF_K)) { - if (BPF_SRC(insn->code) == BPF_X) + if (BPF_SRC(insn->code) == BPF_X) { + /* check in case the register contains a big + * 64-bit value + */ + if (regs[insn->src_reg].imm < -MAX_BPF_STACK || + regs[insn->src_reg].imm > MAX_BPF_STACK) { + verbose("R%d value too big in R%d pointer arithmetic\n", + insn->src_reg, insn->dst_reg); + return -EACCES; + } dst_reg->imm += regs[insn->src_reg].imm; - else + } else { + /* safe against overflow: addition of 32-bit + * numbers in 64-bit representation + */ dst_reg->imm += insn->imm; + } + if (dst_reg->imm > 0 || dst_reg->imm < -MAX_BPF_STACK) { + verbose("R%d out-of-bounds pointer arithmetic\n", + insn->dst_reg); + return -EACCES; + } return 0; } else if (opcode == BPF_ADD && BPF_CLASS(insn->code) == BPF_ALU64 &&
Patches currently in stable-queue which might be from daniel@iogearbox.net are
queue-4.9/bpf-fix-branch-pruning-logic.patch queue-4.9/bpf-adjust-insn_aux_data-when-patching-insns.patch queue-4.9/bpf-fix-incorrect-sign-extension-in-check_alu_op.patch queue-4.9/bpf-reject-out-of-bounds-stack-pointer-calculation.patch
From: Jann Horn jannh@google.com
[ Upstream commit 95a762e2c8c942780948091f8f2a4f32fce1ac6f ]
Distinguish between BPF_ALU64|BPF_MOV|BPF_K (load 32-bit immediate, sign-extended to 64-bit) and BPF_ALU|BPF_MOV|BPF_K (load 32-bit immediate, zero-padded to 64-bit); only perform sign extension in the first case.
Starting with v4.14, this is exploitable by unprivileged users as long as the unprivileged_bpf_disabled sysctl isn't set.
Debian assigned CVE-2017-16995 for this issue.
v3: - add CVE number (Ben Hutchings)
Fixes: 484611357c19 ("bpf: allow access into map value arrays") Signed-off-by: Jann Horn jannh@google.com Acked-by: Edward Cree ecree@solarflare.com Signed-off-by: Alexei Starovoitov ast@kernel.org Signed-off-by: Daniel Borkmann daniel@iogearbox.net --- kernel/bpf/verifier.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index b03af36..8b1ebe4 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -1790,10 +1790,17 @@ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn) /* case: R = imm * remember the value we stored into this reg */ + u64 imm; + + if (BPF_CLASS(insn->code) == BPF_ALU64) + imm = insn->imm; + else + imm = (u32)insn->imm; + regs[insn->dst_reg].type = CONST_IMM; - regs[insn->dst_reg].imm = insn->imm; - regs[insn->dst_reg].max_value = insn->imm; - regs[insn->dst_reg].min_value = insn->imm; + regs[insn->dst_reg].imm = imm; + regs[insn->dst_reg].max_value = imm; + regs[insn->dst_reg].min_value = imm; }
} else if (opcode > BPF_END) {
This is a note to let you know that I've just added the patch titled
bpf: fix incorrect sign extension in check_alu_op()
to the 4.9-stable tree which can be found at: http://www.kernel.org/git/?p=linux/kernel/git/stable/stable-queue.git%3Ba=su...
The filename of the patch is: bpf-fix-incorrect-sign-extension-in-check_alu_op.patch and it can be found in the queue-4.9 subdirectory.
If you, or anyone else, feels it should not be added to the stable tree, please let stable@vger.kernel.org know about it.
From foo@baz Fri Dec 22 16:57:35 CET 2017
From: Daniel Borkmann daniel@iogearbox.net Date: Fri, 22 Dec 2017 16:29:05 +0100 Subject: bpf: fix incorrect sign extension in check_alu_op() To: gregkh@linuxfoundation.org Cc: ast@kernel.org, daniel@iogearbox.net, jannh@google.com, stable@vger.kernel.org Message-ID: 20171222152905.3455-5-daniel@iogearbox.net
From: Daniel Borkmann daniel@iogearbox.net
From: Jann Horn jannh@google.com
[ Upstream commit 95a762e2c8c942780948091f8f2a4f32fce1ac6f ]
Distinguish between BPF_ALU64|BPF_MOV|BPF_K (load 32-bit immediate, sign-extended to 64-bit) and BPF_ALU|BPF_MOV|BPF_K (load 32-bit immediate, zero-padded to 64-bit); only perform sign extension in the first case.
Starting with v4.14, this is exploitable by unprivileged users as long as the unprivileged_bpf_disabled sysctl isn't set.
Debian assigned CVE-2017-16995 for this issue.
v3: - add CVE number (Ben Hutchings)
Fixes: 484611357c19 ("bpf: allow access into map value arrays") Signed-off-by: Jann Horn jannh@google.com Acked-by: Edward Cree ecree@solarflare.com Signed-off-by: Alexei Starovoitov ast@kernel.org Signed-off-by: Daniel Borkmann daniel@iogearbox.net Signed-off-by: Greg Kroah-Hartman gregkh@linuxfoundation.org --- kernel/bpf/verifier.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-)
--- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -1790,10 +1790,17 @@ static int check_alu_op(struct bpf_verif /* case: R = imm * remember the value we stored into this reg */ + u64 imm; + + if (BPF_CLASS(insn->code) == BPF_ALU64) + imm = insn->imm; + else + imm = (u32)insn->imm; + regs[insn->dst_reg].type = CONST_IMM; - regs[insn->dst_reg].imm = insn->imm; - regs[insn->dst_reg].max_value = insn->imm; - regs[insn->dst_reg].min_value = insn->imm; + regs[insn->dst_reg].imm = imm; + regs[insn->dst_reg].max_value = imm; + regs[insn->dst_reg].min_value = imm; }
} else if (opcode > BPF_END) {
Patches currently in stable-queue which might be from daniel@iogearbox.net are
queue-4.9/bpf-fix-branch-pruning-logic.patch queue-4.9/bpf-adjust-insn_aux_data-when-patching-insns.patch queue-4.9/bpf-fix-incorrect-sign-extension-in-check_alu_op.patch queue-4.9/bpf-reject-out-of-bounds-stack-pointer-calculation.patch
linux-stable-mirror@lists.linaro.org