From: Greg Kroah-Hartman Date: Thu, 21 Nov 2019 22:52:46 +0000 (+0100) Subject: 4.19-stable patches X-Git-Tag: v5.3.13~9 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=42acc6549a8937ade9be3e4ada207ba1c028bcfe;p=thirdparty%2Fkernel%2Fstable-queue.git 4.19-stable patches added patches: bpf-x32-fix-bug-for-bpf_alu64-bpf_neg.patch bpf-x32-fix-bug-for-bpf_jmp-bpf_jsgt-bpf_jsle-bpf_jslt-bpf_jsge.patch bpf-x32-fix-bug-with-alu64-lsh-rsh-arsh-bpf_k-shift-by-0.patch bpf-x32-fix-bug-with-alu64-lsh-rsh-arsh-bpf_x-shift-by-0.patch --- diff --git a/queue-4.19/bpf-x32-fix-bug-for-bpf_alu64-bpf_neg.patch b/queue-4.19/bpf-x32-fix-bug-for-bpf_alu64-bpf_neg.patch new file mode 100644 index 00000000000..831ee62a7f4 --- /dev/null +++ b/queue-4.19/bpf-x32-fix-bug-for-bpf_alu64-bpf_neg.patch @@ -0,0 +1,82 @@ +From b9aa0b35d878dff9ed19f94101fe353a4de00cc4 Mon Sep 17 00:00:00 2001 +From: Wang YanQing +Date: Sun, 28 Apr 2019 10:33:02 +0800 +Subject: bpf, x32: Fix bug for BPF_ALU64 | BPF_NEG + +From: Wang YanQing + +commit b9aa0b35d878dff9ed19f94101fe353a4de00cc4 upstream. + +The current implementation has two errors: + +1: The second xor instruction will clear carry flag which + is necessary for following sbb instruction. +2: The select coding for sbb instruction is wrong, the coding + is "sbb dreg_hi,ecx", but what we need is "sbb ecx,dreg_hi". + +This patch rewrites the implementation and fixes the errors. + +This patch fixes below errors reported by bpf/test_verifier in x32 +platform when the jit is enabled: + +" +0: (b4) w1 = 4 +1: (b4) w2 = 4 +2: (1f) r2 -= r1 +3: (4f) r2 |= r1 +4: (87) r2 = -r2 +5: (c7) r2 s>>= 63 +6: (5f) r1 &= r2 +7: (bf) r0 = r1 +8: (95) exit +processed 9 insns (limit 131072), stack depth 0 +0: (b4) w1 = 4 +1: (b4) w2 = 4 +2: (1f) r2 -= r1 +3: (4f) r2 |= r1 +4: (87) r2 = -r2 +5: (c7) r2 s>>= 63 +6: (5f) r1 &= r2 +7: (bf) r0 = r1 +8: (95) exit +processed 9 insns (limit 131072), stack depth 0 +...... +Summary: 1189 PASSED, 125 SKIPPED, 15 FAILED +" + +Signed-off-by: Wang YanQing +Signed-off-by: Daniel Borkmann +Signed-off-by: Greg Kroah-Hartman + +--- + arch/x86/net/bpf_jit_comp32.c | 19 ++++++------------- + 1 file changed, 6 insertions(+), 13 deletions(-) + +--- a/arch/x86/net/bpf_jit_comp32.c ++++ b/arch/x86/net/bpf_jit_comp32.c +@@ -698,19 +698,12 @@ static inline void emit_ia32_neg64(const + STACK_VAR(dst_hi)); + } + +- /* xor ecx,ecx */ +- EMIT2(0x31, add_2reg(0xC0, IA32_ECX, IA32_ECX)); +- /* sub dreg_lo,ecx */ +- EMIT2(0x2B, add_2reg(0xC0, dreg_lo, IA32_ECX)); +- /* mov dreg_lo,ecx */ +- EMIT2(0x89, add_2reg(0xC0, dreg_lo, IA32_ECX)); +- +- /* xor ecx,ecx */ +- EMIT2(0x31, add_2reg(0xC0, IA32_ECX, IA32_ECX)); +- /* sbb dreg_hi,ecx */ +- EMIT2(0x19, add_2reg(0xC0, dreg_hi, IA32_ECX)); +- /* mov dreg_hi,ecx */ +- EMIT2(0x89, add_2reg(0xC0, dreg_hi, IA32_ECX)); ++ /* neg dreg_lo */ ++ EMIT2(0xF7, add_1reg(0xD8, dreg_lo)); ++ /* adc dreg_hi,0x0 */ ++ EMIT3(0x83, add_1reg(0xD0, dreg_hi), 0x00); ++ /* neg dreg_hi */ ++ EMIT2(0xF7, add_1reg(0xD8, dreg_hi)); + + if (dstk) { + /* mov dword ptr [ebp+off],dreg_lo */ diff --git a/queue-4.19/bpf-x32-fix-bug-for-bpf_jmp-bpf_jsgt-bpf_jsle-bpf_jslt-bpf_jsge.patch b/queue-4.19/bpf-x32-fix-bug-for-bpf_jmp-bpf_jsgt-bpf_jsle-bpf_jslt-bpf_jsge.patch new file mode 100644 index 00000000000..01dfe1ad47e --- /dev/null +++ b/queue-4.19/bpf-x32-fix-bug-for-bpf_jmp-bpf_jsgt-bpf_jsle-bpf_jslt-bpf_jsge.patch @@ -0,0 +1,314 @@ +From 711aef1bbf88212a21f7103e88f397b47a528805 Mon Sep 17 00:00:00 2001 +From: Wang YanQing +Date: Sat, 27 Apr 2019 16:28:26 +0800 +Subject: bpf, x32: Fix bug for BPF_JMP | {BPF_JSGT, BPF_JSLE, BPF_JSLT, BPF_JSGE} + +From: Wang YanQing + +commit 711aef1bbf88212a21f7103e88f397b47a528805 upstream. + +The current method to compare 64-bit numbers for conditional jump is: + +1) Compare the high 32-bit first. + +2) If the high 32-bit isn't the same, then goto step 4. + +3) Compare the low 32-bit. + +4) Check the desired condition. + +This method is right for unsigned comparison, but it is buggy for signed +comparison, because it does signed comparison for low 32-bit too. + +There is only one sign bit in 64-bit number, that is the MSB in the 64-bit +number, it is wrong to treat low 32-bit as signed number and do the signed +comparison for it. + +This patch fixes the bug and adds a testcase in selftests/bpf for such bug. + +Link: https://bugzilla.kernel.org/show_bug.cgi?id=205469 +Reported-by: Tony Ambardar +Cc: Tony Ambardar +Cc: stable@vger.kernel.org #v4.19 +Signed-off-by: Wang YanQing +Signed-off-by: Daniel Borkmann +Signed-off-by: Greg Kroah-Hartman + +--- + arch/x86/net/bpf_jit_comp32.c | 221 +++++++++++++++++++++++++++++++----------- + 1 file changed, 168 insertions(+), 53 deletions(-) + +--- a/arch/x86/net/bpf_jit_comp32.c ++++ b/arch/x86/net/bpf_jit_comp32.c +@@ -117,6 +117,8 @@ static bool is_simm32(s64 value) + #define IA32_JLE 0x7E + #define IA32_JG 0x7F + ++#define COND_JMP_OPCODE_INVALID (0xFF) ++ + /* + * Map eBPF registers to IA32 32bit registers or stack scratch space. + * +@@ -1380,6 +1382,75 @@ static inline void emit_push_r64(const u + *pprog = prog; + } + ++static u8 get_cond_jmp_opcode(const u8 op, bool is_cmp_lo) ++{ ++ u8 jmp_cond; ++ ++ /* Convert BPF opcode to x86 */ ++ switch (op) { ++ case BPF_JEQ: ++ jmp_cond = IA32_JE; ++ break; ++ case BPF_JSET: ++ case BPF_JNE: ++ jmp_cond = IA32_JNE; ++ break; ++ case BPF_JGT: ++ /* GT is unsigned '>', JA in x86 */ ++ jmp_cond = IA32_JA; ++ break; ++ case BPF_JLT: ++ /* LT is unsigned '<', JB in x86 */ ++ jmp_cond = IA32_JB; ++ break; ++ case BPF_JGE: ++ /* GE is unsigned '>=', JAE in x86 */ ++ jmp_cond = IA32_JAE; ++ break; ++ case BPF_JLE: ++ /* LE is unsigned '<=', JBE in x86 */ ++ jmp_cond = IA32_JBE; ++ break; ++ case BPF_JSGT: ++ if (!is_cmp_lo) ++ /* Signed '>', GT in x86 */ ++ jmp_cond = IA32_JG; ++ else ++ /* GT is unsigned '>', JA in x86 */ ++ jmp_cond = IA32_JA; ++ break; ++ case BPF_JSLT: ++ if (!is_cmp_lo) ++ /* Signed '<', LT in x86 */ ++ jmp_cond = IA32_JL; ++ else ++ /* LT is unsigned '<', JB in x86 */ ++ jmp_cond = IA32_JB; ++ break; ++ case BPF_JSGE: ++ if (!is_cmp_lo) ++ /* Signed '>=', GE in x86 */ ++ jmp_cond = IA32_JGE; ++ else ++ /* GE is unsigned '>=', JAE in x86 */ ++ jmp_cond = IA32_JAE; ++ break; ++ case BPF_JSLE: ++ if (!is_cmp_lo) ++ /* Signed '<=', LE in x86 */ ++ jmp_cond = IA32_JLE; ++ else ++ /* LE is unsigned '<=', JBE in x86 */ ++ jmp_cond = IA32_JBE; ++ break; ++ default: /* to silence GCC warning */ ++ jmp_cond = COND_JMP_OPCODE_INVALID; ++ break; ++ } ++ ++ return jmp_cond; ++} ++ + static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image, + int oldproglen, struct jit_context *ctx) + { +@@ -1835,11 +1906,7 @@ static int do_jit(struct bpf_prog *bpf_p + case BPF_JMP | BPF_JGT | BPF_X: + case BPF_JMP | BPF_JLT | BPF_X: + case BPF_JMP | BPF_JGE | BPF_X: +- case BPF_JMP | BPF_JLE | BPF_X: +- case BPF_JMP | BPF_JSGT | BPF_X: +- case BPF_JMP | BPF_JSLE | BPF_X: +- case BPF_JMP | BPF_JSLT | BPF_X: +- case BPF_JMP | BPF_JSGE | BPF_X: { ++ case BPF_JMP | BPF_JLE | BPF_X: { + u8 dreg_lo = dstk ? IA32_EAX : dst_lo; + u8 dreg_hi = dstk ? IA32_EDX : dst_hi; + u8 sreg_lo = sstk ? IA32_ECX : src_lo; +@@ -1866,6 +1933,40 @@ static int do_jit(struct bpf_prog *bpf_p + EMIT2(0x39, add_2reg(0xC0, dreg_lo, sreg_lo)); + goto emit_cond_jmp; + } ++ case BPF_JMP | BPF_JSGT | BPF_X: ++ case BPF_JMP | BPF_JSLE | BPF_X: ++ case BPF_JMP | BPF_JSLT | BPF_X: ++ case BPF_JMP | BPF_JSGE | BPF_X: { ++ u8 dreg_lo = dstk ? IA32_EAX : dst_lo; ++ u8 dreg_hi = dstk ? IA32_EDX : dst_hi; ++ u8 sreg_lo = sstk ? IA32_ECX : src_lo; ++ u8 sreg_hi = sstk ? IA32_EBX : src_hi; ++ ++ if (dstk) { ++ EMIT3(0x8B, add_2reg(0x40, IA32_EBP, IA32_EAX), ++ STACK_VAR(dst_lo)); ++ EMIT3(0x8B, ++ add_2reg(0x40, IA32_EBP, ++ IA32_EDX), ++ STACK_VAR(dst_hi)); ++ } ++ ++ if (sstk) { ++ EMIT3(0x8B, add_2reg(0x40, IA32_EBP, IA32_ECX), ++ STACK_VAR(src_lo)); ++ EMIT3(0x8B, ++ add_2reg(0x40, IA32_EBP, ++ IA32_EBX), ++ STACK_VAR(src_hi)); ++ } ++ ++ /* cmp dreg_hi,sreg_hi */ ++ EMIT2(0x39, add_2reg(0xC0, dreg_hi, sreg_hi)); ++ EMIT2(IA32_JNE, 10); ++ /* cmp dreg_lo,sreg_lo */ ++ EMIT2(0x39, add_2reg(0xC0, dreg_lo, sreg_lo)); ++ goto emit_cond_jmp_signed; ++ } + case BPF_JMP | BPF_JSET | BPF_X: { + u8 dreg_lo = dstk ? IA32_EAX : dst_lo; + u8 dreg_hi = dstk ? IA32_EDX : dst_hi; +@@ -1926,11 +2027,7 @@ static int do_jit(struct bpf_prog *bpf_p + case BPF_JMP | BPF_JGT | BPF_K: + case BPF_JMP | BPF_JLT | BPF_K: + case BPF_JMP | BPF_JGE | BPF_K: +- case BPF_JMP | BPF_JLE | BPF_K: +- case BPF_JMP | BPF_JSGT | BPF_K: +- case BPF_JMP | BPF_JSLE | BPF_K: +- case BPF_JMP | BPF_JSLT | BPF_K: +- case BPF_JMP | BPF_JSGE | BPF_K: { ++ case BPF_JMP | BPF_JLE | BPF_K: { + u32 hi; + u8 dreg_lo = dstk ? IA32_EAX : dst_lo; + u8 dreg_hi = dstk ? IA32_EDX : dst_hi; +@@ -1956,50 +2053,9 @@ static int do_jit(struct bpf_prog *bpf_p + /* cmp dreg_lo,sreg_lo */ + EMIT2(0x39, add_2reg(0xC0, dreg_lo, sreg_lo)); + +-emit_cond_jmp: /* Convert BPF opcode to x86 */ +- switch (BPF_OP(code)) { +- case BPF_JEQ: +- jmp_cond = IA32_JE; +- break; +- case BPF_JSET: +- case BPF_JNE: +- jmp_cond = IA32_JNE; +- break; +- case BPF_JGT: +- /* GT is unsigned '>', JA in x86 */ +- jmp_cond = IA32_JA; +- break; +- case BPF_JLT: +- /* LT is unsigned '<', JB in x86 */ +- jmp_cond = IA32_JB; +- break; +- case BPF_JGE: +- /* GE is unsigned '>=', JAE in x86 */ +- jmp_cond = IA32_JAE; +- break; +- case BPF_JLE: +- /* LE is unsigned '<=', JBE in x86 */ +- jmp_cond = IA32_JBE; +- break; +- case BPF_JSGT: +- /* Signed '>', GT in x86 */ +- jmp_cond = IA32_JG; +- break; +- case BPF_JSLT: +- /* Signed '<', LT in x86 */ +- jmp_cond = IA32_JL; +- break; +- case BPF_JSGE: +- /* Signed '>=', GE in x86 */ +- jmp_cond = IA32_JGE; +- break; +- case BPF_JSLE: +- /* Signed '<=', LE in x86 */ +- jmp_cond = IA32_JLE; +- break; +- default: /* to silence GCC warning */ ++emit_cond_jmp: jmp_cond = get_cond_jmp_opcode(BPF_OP(code), false); ++ if (jmp_cond == COND_JMP_OPCODE_INVALID) + return -EFAULT; +- } + jmp_offset = addrs[i + insn->off] - addrs[i]; + if (is_imm8(jmp_offset)) { + EMIT2(jmp_cond, jmp_offset); +@@ -2009,7 +2065,66 @@ emit_cond_jmp: /* Convert BPF opcode to + pr_err("cond_jmp gen bug %llx\n", jmp_offset); + return -EFAULT; + } ++ break; ++ } ++ case BPF_JMP | BPF_JSGT | BPF_K: ++ case BPF_JMP | BPF_JSLE | BPF_K: ++ case BPF_JMP | BPF_JSLT | BPF_K: ++ case BPF_JMP | BPF_JSGE | BPF_K: { ++ u8 dreg_lo = dstk ? IA32_EAX : dst_lo; ++ u8 dreg_hi = dstk ? IA32_EDX : dst_hi; ++ u8 sreg_lo = IA32_ECX; ++ u8 sreg_hi = IA32_EBX; ++ u32 hi; ++ ++ if (dstk) { ++ EMIT3(0x8B, add_2reg(0x40, IA32_EBP, IA32_EAX), ++ STACK_VAR(dst_lo)); ++ EMIT3(0x8B, ++ add_2reg(0x40, IA32_EBP, ++ IA32_EDX), ++ STACK_VAR(dst_hi)); ++ } ++ ++ /* mov ecx,imm32 */ ++ EMIT2_off32(0xC7, add_1reg(0xC0, IA32_ECX), imm32); ++ hi = imm32 & (1 << 31) ? (u32)~0 : 0; ++ /* mov ebx,imm32 */ ++ EMIT2_off32(0xC7, add_1reg(0xC0, IA32_EBX), hi); ++ /* cmp dreg_hi,sreg_hi */ ++ EMIT2(0x39, add_2reg(0xC0, dreg_hi, sreg_hi)); ++ EMIT2(IA32_JNE, 10); ++ /* cmp dreg_lo,sreg_lo */ ++ EMIT2(0x39, add_2reg(0xC0, dreg_lo, sreg_lo)); ++ ++ /* ++ * For simplicity of branch offset computation, ++ * let's use fixed jump coding here. ++ */ ++emit_cond_jmp_signed: /* Check the condition for low 32-bit comparison */ ++ jmp_cond = get_cond_jmp_opcode(BPF_OP(code), true); ++ if (jmp_cond == COND_JMP_OPCODE_INVALID) ++ return -EFAULT; ++ jmp_offset = addrs[i + insn->off] - addrs[i] + 8; ++ if (is_simm32(jmp_offset)) { ++ EMIT2_off32(0x0F, jmp_cond + 0x10, jmp_offset); ++ } else { ++ pr_err("cond_jmp gen bug %llx\n", jmp_offset); ++ return -EFAULT; ++ } ++ EMIT2(0xEB, 6); + ++ /* Check the condition for high 32-bit comparison */ ++ jmp_cond = get_cond_jmp_opcode(BPF_OP(code), false); ++ if (jmp_cond == COND_JMP_OPCODE_INVALID) ++ return -EFAULT; ++ jmp_offset = addrs[i + insn->off] - addrs[i]; ++ if (is_simm32(jmp_offset)) { ++ EMIT2_off32(0x0F, jmp_cond + 0x10, jmp_offset); ++ } else { ++ pr_err("cond_jmp gen bug %llx\n", jmp_offset); ++ return -EFAULT; ++ } + break; + } + case BPF_JMP | BPF_JA: diff --git a/queue-4.19/bpf-x32-fix-bug-with-alu64-lsh-rsh-arsh-bpf_k-shift-by-0.patch b/queue-4.19/bpf-x32-fix-bug-with-alu64-lsh-rsh-arsh-bpf_k-shift-by-0.patch new file mode 100644 index 00000000000..21a7f9855a7 --- /dev/null +++ b/queue-4.19/bpf-x32-fix-bug-with-alu64-lsh-rsh-arsh-bpf_k-shift-by-0.patch @@ -0,0 +1,130 @@ +From 6fa632e719eec4d1b1ebf3ddc0b2d667997b057b Mon Sep 17 00:00:00 2001 +From: Luke Nelson +Date: Fri, 28 Jun 2019 22:57:50 -0700 +Subject: bpf, x32: Fix bug with ALU64 {LSH, RSH, ARSH} BPF_K shift by 0 + +From: Luke Nelson + +commit 6fa632e719eec4d1b1ebf3ddc0b2d667997b057b upstream. + +The current x32 BPF JIT does not correctly compile shift operations when +the immediate shift amount is 0. The expected behavior is for this to +be a no-op. + +The following program demonstrates the bug. The expexceted result is 1, +but the current JITed code returns 2. + + r0 = 1 + r1 = 1 + r1 <<= 0 + if r1 == 1 goto end + r0 = 2 +end: + exit + +This patch simplifies the code and fixes the bug. + +Fixes: 03f5781be2c7 ("bpf, x86_32: add eBPF JIT compiler for ia32") +Co-developed-by: Xi Wang +Signed-off-by: Xi Wang +Signed-off-by: Luke Nelson +Signed-off-by: Daniel Borkmann +Signed-off-by: Wang YanQing +Signed-off-by: Greg Kroah-Hartman + +--- + arch/x86/net/bpf_jit_comp32.c | 63 ++++-------------------------------------- + 1 file changed, 6 insertions(+), 57 deletions(-) + +--- a/arch/x86/net/bpf_jit_comp32.c ++++ b/arch/x86/net/bpf_jit_comp32.c +@@ -892,27 +892,10 @@ static inline void emit_ia32_lsh_i64(con + } + /* Do LSH operation */ + if (val < 32) { +- /* shl dreg_hi,imm8 */ +- EMIT3(0xC1, add_1reg(0xE0, dreg_hi), val); +- /* mov ebx,dreg_lo */ +- EMIT2(0x8B, add_2reg(0xC0, dreg_lo, IA32_EBX)); ++ /* shld dreg_hi,dreg_lo,imm8 */ ++ EMIT4(0x0F, 0xA4, add_2reg(0xC0, dreg_hi, dreg_lo), val); + /* shl dreg_lo,imm8 */ + EMIT3(0xC1, add_1reg(0xE0, dreg_lo), val); +- +- /* IA32_ECX = 32 - val */ +- /* mov ecx,val */ +- EMIT2(0xB1, val); +- /* movzx ecx,ecx */ +- EMIT3(0x0F, 0xB6, add_2reg(0xC0, IA32_ECX, IA32_ECX)); +- /* neg ecx */ +- EMIT2(0xF7, add_1reg(0xD8, IA32_ECX)); +- /* add ecx,32 */ +- EMIT3(0x83, add_1reg(0xC0, IA32_ECX), 32); +- +- /* shr ebx,cl */ +- EMIT2(0xD3, add_1reg(0xE8, IA32_EBX)); +- /* or dreg_hi,ebx */ +- EMIT2(0x09, add_2reg(0xC0, dreg_hi, IA32_EBX)); + } else if (val >= 32 && val < 64) { + u32 value = val - 32; + +@@ -958,27 +941,10 @@ static inline void emit_ia32_rsh_i64(con + + /* Do RSH operation */ + if (val < 32) { +- /* shr dreg_lo,imm8 */ +- EMIT3(0xC1, add_1reg(0xE8, dreg_lo), val); +- /* mov ebx,dreg_hi */ +- EMIT2(0x8B, add_2reg(0xC0, dreg_hi, IA32_EBX)); ++ /* shrd dreg_lo,dreg_hi,imm8 */ ++ EMIT4(0x0F, 0xAC, add_2reg(0xC0, dreg_lo, dreg_hi), val); + /* shr dreg_hi,imm8 */ + EMIT3(0xC1, add_1reg(0xE8, dreg_hi), val); +- +- /* IA32_ECX = 32 - val */ +- /* mov ecx,val */ +- EMIT2(0xB1, val); +- /* movzx ecx,ecx */ +- EMIT3(0x0F, 0xB6, add_2reg(0xC0, IA32_ECX, IA32_ECX)); +- /* neg ecx */ +- EMIT2(0xF7, add_1reg(0xD8, IA32_ECX)); +- /* add ecx,32 */ +- EMIT3(0x83, add_1reg(0xC0, IA32_ECX), 32); +- +- /* shl ebx,cl */ +- EMIT2(0xD3, add_1reg(0xE0, IA32_EBX)); +- /* or dreg_lo,ebx */ +- EMIT2(0x09, add_2reg(0xC0, dreg_lo, IA32_EBX)); + } else if (val >= 32 && val < 64) { + u32 value = val - 32; + +@@ -1023,27 +989,10 @@ static inline void emit_ia32_arsh_i64(co + } + /* Do RSH operation */ + if (val < 32) { +- /* shr dreg_lo,imm8 */ +- EMIT3(0xC1, add_1reg(0xE8, dreg_lo), val); +- /* mov ebx,dreg_hi */ +- EMIT2(0x8B, add_2reg(0xC0, dreg_hi, IA32_EBX)); ++ /* shrd dreg_lo,dreg_hi,imm8 */ ++ EMIT4(0x0F, 0xAC, add_2reg(0xC0, dreg_lo, dreg_hi), val); + /* ashr dreg_hi,imm8 */ + EMIT3(0xC1, add_1reg(0xF8, dreg_hi), val); +- +- /* IA32_ECX = 32 - val */ +- /* mov ecx,val */ +- EMIT2(0xB1, val); +- /* movzx ecx,ecx */ +- EMIT3(0x0F, 0xB6, add_2reg(0xC0, IA32_ECX, IA32_ECX)); +- /* neg ecx */ +- EMIT2(0xF7, add_1reg(0xD8, IA32_ECX)); +- /* add ecx,32 */ +- EMIT3(0x83, add_1reg(0xC0, IA32_ECX), 32); +- +- /* shl ebx,cl */ +- EMIT2(0xD3, add_1reg(0xE0, IA32_EBX)); +- /* or dreg_lo,ebx */ +- EMIT2(0x09, add_2reg(0xC0, dreg_lo, IA32_EBX)); + } else if (val >= 32 && val < 64) { + u32 value = val - 32; + diff --git a/queue-4.19/bpf-x32-fix-bug-with-alu64-lsh-rsh-arsh-bpf_x-shift-by-0.patch b/queue-4.19/bpf-x32-fix-bug-with-alu64-lsh-rsh-arsh-bpf_x-shift-by-0.patch new file mode 100644 index 00000000000..eb69b4052d1 --- /dev/null +++ b/queue-4.19/bpf-x32-fix-bug-with-alu64-lsh-rsh-arsh-bpf_x-shift-by-0.patch @@ -0,0 +1,337 @@ +From 68a8357ec15bdce55266e9fba8b8b3b8143fa7d2 Mon Sep 17 00:00:00 2001 +From: Luke Nelson +Date: Fri, 28 Jun 2019 22:57:49 -0700 +Subject: bpf, x32: Fix bug with ALU64 {LSH, RSH, ARSH} BPF_X shift by 0 + +From: Luke Nelson + +commit 68a8357ec15bdce55266e9fba8b8b3b8143fa7d2 upstream. + +The current x32 BPF JIT for shift operations is not correct when the +shift amount in a register is 0. The expected behavior is a no-op, whereas +the current implementation changes bits in the destination register. + +The following example demonstrates the bug. The expected result of this +program is 1, but the current JITed code returns 2. + + r0 = 1 + r1 = 1 + r2 = 0 + r1 <<= r2 + if r1 == 1 goto end + r0 = 2 +end: + exit + +The bug is caused by an incorrect assumption by the JIT that a shift by +32 clear the register. On x32 however, shifts use the lower 5 bits of +the source, making a shift by 32 equivalent to a shift by 0. + +This patch fixes the bug using double-precision shifts, which also +simplifies the code. + +Fixes: 03f5781be2c7 ("bpf, x86_32: add eBPF JIT compiler for ia32") +Co-developed-by: Xi Wang +Signed-off-by: Xi Wang +Signed-off-by: Luke Nelson +Signed-off-by: Daniel Borkmann +Signed-off-by: Wang YanQing +Signed-off-by: Greg Kroah-Hartman + +--- + arch/x86/net/bpf_jit_comp32.c | 221 ++++-------------------------------------- + 1 file changed, 23 insertions(+), 198 deletions(-) + +--- a/arch/x86/net/bpf_jit_comp32.c ++++ b/arch/x86/net/bpf_jit_comp32.c +@@ -722,9 +722,6 @@ static inline void emit_ia32_lsh_r64(con + { + u8 *prog = *pprog; + int cnt = 0; +- static int jmp_label1 = -1; +- static int jmp_label2 = -1; +- static int jmp_label3 = -1; + u8 dreg_lo = dstk ? IA32_EAX : dst_lo; + u8 dreg_hi = dstk ? IA32_EDX : dst_hi; + +@@ -743,79 +740,23 @@ static inline void emit_ia32_lsh_r64(con + /* mov ecx,src_lo */ + EMIT2(0x8B, add_2reg(0xC0, src_lo, IA32_ECX)); + +- /* cmp ecx,32 */ +- EMIT3(0x83, add_1reg(0xF8, IA32_ECX), 32); +- /* Jumps when >= 32 */ +- if (is_imm8(jmp_label(jmp_label1, 2))) +- EMIT2(IA32_JAE, jmp_label(jmp_label1, 2)); +- else +- EMIT2_off32(0x0F, IA32_JAE + 0x10, jmp_label(jmp_label1, 6)); +- +- /* < 32 */ +- /* shl dreg_hi,cl */ +- EMIT2(0xD3, add_1reg(0xE0, dreg_hi)); +- /* mov ebx,dreg_lo */ +- EMIT2(0x8B, add_2reg(0xC0, dreg_lo, IA32_EBX)); ++ /* shld dreg_hi,dreg_lo,cl */ ++ EMIT3(0x0F, 0xA5, add_2reg(0xC0, dreg_hi, dreg_lo)); + /* shl dreg_lo,cl */ + EMIT2(0xD3, add_1reg(0xE0, dreg_lo)); + +- /* IA32_ECX = -IA32_ECX + 32 */ +- /* neg ecx */ +- EMIT2(0xF7, add_1reg(0xD8, IA32_ECX)); +- /* add ecx,32 */ +- EMIT3(0x83, add_1reg(0xC0, IA32_ECX), 32); +- +- /* shr ebx,cl */ +- EMIT2(0xD3, add_1reg(0xE8, IA32_EBX)); +- /* or dreg_hi,ebx */ +- EMIT2(0x09, add_2reg(0xC0, dreg_hi, IA32_EBX)); +- +- /* goto out; */ +- if (is_imm8(jmp_label(jmp_label3, 2))) +- EMIT2(0xEB, jmp_label(jmp_label3, 2)); +- else +- EMIT1_off32(0xE9, jmp_label(jmp_label3, 5)); ++ /* if ecx >= 32, mov dreg_lo into dreg_hi and clear dreg_lo */ + +- /* >= 32 */ +- if (jmp_label1 == -1) +- jmp_label1 = cnt; +- +- /* cmp ecx,64 */ +- EMIT3(0x83, add_1reg(0xF8, IA32_ECX), 64); +- /* Jumps when >= 64 */ +- if (is_imm8(jmp_label(jmp_label2, 2))) +- EMIT2(IA32_JAE, jmp_label(jmp_label2, 2)); +- else +- EMIT2_off32(0x0F, IA32_JAE + 0x10, jmp_label(jmp_label2, 6)); ++ /* cmp ecx,32 */ ++ EMIT3(0x83, add_1reg(0xF8, IA32_ECX), 32); ++ /* skip the next two instructions (4 bytes) when < 32 */ ++ EMIT2(IA32_JB, 4); + +- /* >= 32 && < 64 */ +- /* sub ecx,32 */ +- EMIT3(0x83, add_1reg(0xE8, IA32_ECX), 32); +- /* shl dreg_lo,cl */ +- EMIT2(0xD3, add_1reg(0xE0, dreg_lo)); + /* mov dreg_hi,dreg_lo */ + EMIT2(0x89, add_2reg(0xC0, dreg_hi, dreg_lo)); +- + /* xor dreg_lo,dreg_lo */ + EMIT2(0x33, add_2reg(0xC0, dreg_lo, dreg_lo)); + +- /* goto out; */ +- if (is_imm8(jmp_label(jmp_label3, 2))) +- EMIT2(0xEB, jmp_label(jmp_label3, 2)); +- else +- EMIT1_off32(0xE9, jmp_label(jmp_label3, 5)); +- +- /* >= 64 */ +- if (jmp_label2 == -1) +- jmp_label2 = cnt; +- /* xor dreg_lo,dreg_lo */ +- EMIT2(0x33, add_2reg(0xC0, dreg_lo, dreg_lo)); +- /* xor dreg_hi,dreg_hi */ +- EMIT2(0x33, add_2reg(0xC0, dreg_hi, dreg_hi)); +- +- if (jmp_label3 == -1) +- jmp_label3 = cnt; +- + if (dstk) { + /* mov dword ptr [ebp+off],dreg_lo */ + EMIT3(0x89, add_2reg(0x40, IA32_EBP, dreg_lo), +@@ -834,9 +775,6 @@ static inline void emit_ia32_arsh_r64(co + { + u8 *prog = *pprog; + int cnt = 0; +- static int jmp_label1 = -1; +- static int jmp_label2 = -1; +- static int jmp_label3 = -1; + u8 dreg_lo = dstk ? IA32_EAX : dst_lo; + u8 dreg_hi = dstk ? IA32_EDX : dst_hi; + +@@ -855,79 +793,23 @@ static inline void emit_ia32_arsh_r64(co + /* mov ecx,src_lo */ + EMIT2(0x8B, add_2reg(0xC0, src_lo, IA32_ECX)); + +- /* cmp ecx,32 */ +- EMIT3(0x83, add_1reg(0xF8, IA32_ECX), 32); +- /* Jumps when >= 32 */ +- if (is_imm8(jmp_label(jmp_label1, 2))) +- EMIT2(IA32_JAE, jmp_label(jmp_label1, 2)); +- else +- EMIT2_off32(0x0F, IA32_JAE + 0x10, jmp_label(jmp_label1, 6)); +- +- /* < 32 */ +- /* lshr dreg_lo,cl */ +- EMIT2(0xD3, add_1reg(0xE8, dreg_lo)); +- /* mov ebx,dreg_hi */ +- EMIT2(0x8B, add_2reg(0xC0, dreg_hi, IA32_EBX)); +- /* ashr dreg_hi,cl */ ++ /* shrd dreg_lo,dreg_hi,cl */ ++ EMIT3(0x0F, 0xAD, add_2reg(0xC0, dreg_lo, dreg_hi)); ++ /* sar dreg_hi,cl */ + EMIT2(0xD3, add_1reg(0xF8, dreg_hi)); + +- /* IA32_ECX = -IA32_ECX + 32 */ +- /* neg ecx */ +- EMIT2(0xF7, add_1reg(0xD8, IA32_ECX)); +- /* add ecx,32 */ +- EMIT3(0x83, add_1reg(0xC0, IA32_ECX), 32); +- +- /* shl ebx,cl */ +- EMIT2(0xD3, add_1reg(0xE0, IA32_EBX)); +- /* or dreg_lo,ebx */ +- EMIT2(0x09, add_2reg(0xC0, dreg_lo, IA32_EBX)); +- +- /* goto out; */ +- if (is_imm8(jmp_label(jmp_label3, 2))) +- EMIT2(0xEB, jmp_label(jmp_label3, 2)); +- else +- EMIT1_off32(0xE9, jmp_label(jmp_label3, 5)); ++ /* if ecx >= 32, mov dreg_hi to dreg_lo and set/clear dreg_hi depending on sign */ + +- /* >= 32 */ +- if (jmp_label1 == -1) +- jmp_label1 = cnt; +- +- /* cmp ecx,64 */ +- EMIT3(0x83, add_1reg(0xF8, IA32_ECX), 64); +- /* Jumps when >= 64 */ +- if (is_imm8(jmp_label(jmp_label2, 2))) +- EMIT2(IA32_JAE, jmp_label(jmp_label2, 2)); +- else +- EMIT2_off32(0x0F, IA32_JAE + 0x10, jmp_label(jmp_label2, 6)); ++ /* cmp ecx,32 */ ++ EMIT3(0x83, add_1reg(0xF8, IA32_ECX), 32); ++ /* skip the next two instructions (5 bytes) when < 32 */ ++ EMIT2(IA32_JB, 5); + +- /* >= 32 && < 64 */ +- /* sub ecx,32 */ +- EMIT3(0x83, add_1reg(0xE8, IA32_ECX), 32); +- /* ashr dreg_hi,cl */ +- EMIT2(0xD3, add_1reg(0xF8, dreg_hi)); + /* mov dreg_lo,dreg_hi */ + EMIT2(0x89, add_2reg(0xC0, dreg_lo, dreg_hi)); +- +- /* ashr dreg_hi,imm8 */ ++ /* sar dreg_hi,31 */ + EMIT3(0xC1, add_1reg(0xF8, dreg_hi), 31); + +- /* goto out; */ +- if (is_imm8(jmp_label(jmp_label3, 2))) +- EMIT2(0xEB, jmp_label(jmp_label3, 2)); +- else +- EMIT1_off32(0xE9, jmp_label(jmp_label3, 5)); +- +- /* >= 64 */ +- if (jmp_label2 == -1) +- jmp_label2 = cnt; +- /* ashr dreg_hi,imm8 */ +- EMIT3(0xC1, add_1reg(0xF8, dreg_hi), 31); +- /* mov dreg_lo,dreg_hi */ +- EMIT2(0x89, add_2reg(0xC0, dreg_lo, dreg_hi)); +- +- if (jmp_label3 == -1) +- jmp_label3 = cnt; +- + if (dstk) { + /* mov dword ptr [ebp+off],dreg_lo */ + EMIT3(0x89, add_2reg(0x40, IA32_EBP, dreg_lo), +@@ -946,9 +828,6 @@ static inline void emit_ia32_rsh_r64(con + { + u8 *prog = *pprog; + int cnt = 0; +- static int jmp_label1 = -1; +- static int jmp_label2 = -1; +- static int jmp_label3 = -1; + u8 dreg_lo = dstk ? IA32_EAX : dst_lo; + u8 dreg_hi = dstk ? IA32_EDX : dst_hi; + +@@ -967,77 +846,23 @@ static inline void emit_ia32_rsh_r64(con + /* mov ecx,src_lo */ + EMIT2(0x8B, add_2reg(0xC0, src_lo, IA32_ECX)); + +- /* cmp ecx,32 */ +- EMIT3(0x83, add_1reg(0xF8, IA32_ECX), 32); +- /* Jumps when >= 32 */ +- if (is_imm8(jmp_label(jmp_label1, 2))) +- EMIT2(IA32_JAE, jmp_label(jmp_label1, 2)); +- else +- EMIT2_off32(0x0F, IA32_JAE + 0x10, jmp_label(jmp_label1, 6)); +- +- /* < 32 */ +- /* lshr dreg_lo,cl */ +- EMIT2(0xD3, add_1reg(0xE8, dreg_lo)); +- /* mov ebx,dreg_hi */ +- EMIT2(0x8B, add_2reg(0xC0, dreg_hi, IA32_EBX)); ++ /* shrd dreg_lo,dreg_hi,cl */ ++ EMIT3(0x0F, 0xAD, add_2reg(0xC0, dreg_lo, dreg_hi)); + /* shr dreg_hi,cl */ + EMIT2(0xD3, add_1reg(0xE8, dreg_hi)); + +- /* IA32_ECX = -IA32_ECX + 32 */ +- /* neg ecx */ +- EMIT2(0xF7, add_1reg(0xD8, IA32_ECX)); +- /* add ecx,32 */ +- EMIT3(0x83, add_1reg(0xC0, IA32_ECX), 32); +- +- /* shl ebx,cl */ +- EMIT2(0xD3, add_1reg(0xE0, IA32_EBX)); +- /* or dreg_lo,ebx */ +- EMIT2(0x09, add_2reg(0xC0, dreg_lo, IA32_EBX)); +- +- /* goto out; */ +- if (is_imm8(jmp_label(jmp_label3, 2))) +- EMIT2(0xEB, jmp_label(jmp_label3, 2)); +- else +- EMIT1_off32(0xE9, jmp_label(jmp_label3, 5)); ++ /* if ecx >= 32, mov dreg_hi to dreg_lo and clear dreg_hi */ + +- /* >= 32 */ +- if (jmp_label1 == -1) +- jmp_label1 = cnt; +- /* cmp ecx,64 */ +- EMIT3(0x83, add_1reg(0xF8, IA32_ECX), 64); +- /* Jumps when >= 64 */ +- if (is_imm8(jmp_label(jmp_label2, 2))) +- EMIT2(IA32_JAE, jmp_label(jmp_label2, 2)); +- else +- EMIT2_off32(0x0F, IA32_JAE + 0x10, jmp_label(jmp_label2, 6)); ++ /* cmp ecx,32 */ ++ EMIT3(0x83, add_1reg(0xF8, IA32_ECX), 32); ++ /* skip the next two instructions (4 bytes) when < 32 */ ++ EMIT2(IA32_JB, 4); + +- /* >= 32 && < 64 */ +- /* sub ecx,32 */ +- EMIT3(0x83, add_1reg(0xE8, IA32_ECX), 32); +- /* shr dreg_hi,cl */ +- EMIT2(0xD3, add_1reg(0xE8, dreg_hi)); + /* mov dreg_lo,dreg_hi */ + EMIT2(0x89, add_2reg(0xC0, dreg_lo, dreg_hi)); + /* xor dreg_hi,dreg_hi */ + EMIT2(0x33, add_2reg(0xC0, dreg_hi, dreg_hi)); + +- /* goto out; */ +- if (is_imm8(jmp_label(jmp_label3, 2))) +- EMIT2(0xEB, jmp_label(jmp_label3, 2)); +- else +- EMIT1_off32(0xE9, jmp_label(jmp_label3, 5)); +- +- /* >= 64 */ +- if (jmp_label2 == -1) +- jmp_label2 = cnt; +- /* xor dreg_lo,dreg_lo */ +- EMIT2(0x33, add_2reg(0xC0, dreg_lo, dreg_lo)); +- /* xor dreg_hi,dreg_hi */ +- EMIT2(0x33, add_2reg(0xC0, dreg_hi, dreg_hi)); +- +- if (jmp_label3 == -1) +- jmp_label3 = cnt; +- + if (dstk) { + /* mov dword ptr [ebp+off],dreg_lo */ + EMIT3(0x89, add_2reg(0x40, IA32_EBP, dreg_lo), diff --git a/queue-4.19/series b/queue-4.19/series index e7caf57e728..d3294be4287 100644 --- a/queue-4.19/series +++ b/queue-4.19/series @@ -8,3 +8,7 @@ mm-memory_hotplug-don-t-access-uninitialized-memmaps-in-shrink_pgdat_span.patch mm-memory_hotplug-fix-updating-the-node-span.patch arm64-uaccess-ensure-pan-is-re-enabled-after-unhandled-uaccess-fault.patch fbdev-ditch-fb_edid_add_monspecs.patch +bpf-x32-fix-bug-for-bpf_alu64-bpf_neg.patch +bpf-x32-fix-bug-with-alu64-lsh-rsh-arsh-bpf_x-shift-by-0.patch +bpf-x32-fix-bug-with-alu64-lsh-rsh-arsh-bpf_k-shift-by-0.patch +bpf-x32-fix-bug-for-bpf_jmp-bpf_jsgt-bpf_jsle-bpf_jslt-bpf_jsge.patch