--- /dev/null
+From b9aa0b35d878dff9ed19f94101fe353a4de00cc4 Mon Sep 17 00:00:00 2001
+From: Wang YanQing <udknight@gmail.com>
+Date: Sun, 28 Apr 2019 10:33:02 +0800
+Subject: bpf, x32: Fix bug for BPF_ALU64 | BPF_NEG
+
+From: Wang YanQing <udknight@gmail.com>
+
+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 <udknight@gmail.com>
+Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ 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 */
--- /dev/null
+From 711aef1bbf88212a21f7103e88f397b47a528805 Mon Sep 17 00:00:00 2001
+From: Wang YanQing <udknight@gmail.com>
+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 <udknight@gmail.com>
+
+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 <itugrok@yahoo.com>
+Cc: Tony Ambardar <itugrok@yahoo.com>
+Cc: stable@vger.kernel.org #v4.19
+Signed-off-by: Wang YanQing <udknight@gmail.com>
+Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ 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:
--- /dev/null
+From 6fa632e719eec4d1b1ebf3ddc0b2d667997b057b Mon Sep 17 00:00:00 2001
+From: Luke Nelson <lukenels@cs.washington.edu>
+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 <lukenels@cs.washington.edu>
+
+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 <xi.wang@gmail.com>
+Signed-off-by: Xi Wang <xi.wang@gmail.com>
+Signed-off-by: Luke Nelson <luke.r.nels@gmail.com>
+Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
+Signed-off-by: Wang YanQing <udknight@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ 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;
+
--- /dev/null
+From 68a8357ec15bdce55266e9fba8b8b3b8143fa7d2 Mon Sep 17 00:00:00 2001
+From: Luke Nelson <lukenels@cs.washington.edu>
+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 <lukenels@cs.washington.edu>
+
+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 <xi.wang@gmail.com>
+Signed-off-by: Xi Wang <xi.wang@gmail.com>
+Signed-off-by: Luke Nelson <luke.r.nels@gmail.com>
+Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
+Signed-off-by: Wang YanQing <udknight@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ 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),
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