summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLuke Nelson <lukenels@cs.washington.edu>2019-06-29 01:57:49 -0400
committerDaniel Borkmann <daniel@iogearbox.net>2019-07-03 05:14:28 -0400
commit68a8357ec15bdce55266e9fba8b8b3b8143fa7d2 (patch)
tree2a00d404a186fdc829f496f988df13b7fefc7a6b
parent0472301a28f6cf53a6bc5783e48a2d0bbff4682f (diff)
bpf, x32: Fix bug with ALU64 {LSH, RSH, ARSH} BPF_X shift by 0
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>
-rw-r--r--arch/x86/net/bpf_jit_comp32.c221
1 files changed, 23 insertions, 198 deletions
diff --git a/arch/x86/net/bpf_jit_comp32.c b/arch/x86/net/bpf_jit_comp32.c
index b29e82f190c7..f34ef513f4f9 100644
--- a/arch/x86/net/bpf_jit_comp32.c
+++ b/arch/x86/net/bpf_jit_comp32.c
@@ -724,9 +724,6 @@ static inline void emit_ia32_lsh_r64(const u8 dst[], const u8 src[],
724{ 724{
725 u8 *prog = *pprog; 725 u8 *prog = *pprog;
726 int cnt = 0; 726 int cnt = 0;
727 static int jmp_label1 = -1;
728 static int jmp_label2 = -1;
729 static int jmp_label3 = -1;
730 u8 dreg_lo = dstk ? IA32_EAX : dst_lo; 727 u8 dreg_lo = dstk ? IA32_EAX : dst_lo;
731 u8 dreg_hi = dstk ? IA32_EDX : dst_hi; 728 u8 dreg_hi = dstk ? IA32_EDX : dst_hi;
732 729
@@ -745,79 +742,23 @@ static inline void emit_ia32_lsh_r64(const u8 dst[], const u8 src[],
745 /* mov ecx,src_lo */ 742 /* mov ecx,src_lo */
746 EMIT2(0x8B, add_2reg(0xC0, src_lo, IA32_ECX)); 743 EMIT2(0x8B, add_2reg(0xC0, src_lo, IA32_ECX));
747 744
748 /* cmp ecx,32 */ 745 /* shld dreg_hi,dreg_lo,cl */
749 EMIT3(0x83, add_1reg(0xF8, IA32_ECX), 32); 746 EMIT3(0x0F, 0xA5, add_2reg(0xC0, dreg_hi, dreg_lo));
750 /* Jumps when >= 32 */
751 if (is_imm8(jmp_label(jmp_label1, 2)))
752 EMIT2(IA32_JAE, jmp_label(jmp_label1, 2));
753 else
754 EMIT2_off32(0x0F, IA32_JAE + 0x10, jmp_label(jmp_label1, 6));
755
756 /* < 32 */
757 /* shl dreg_hi,cl */
758 EMIT2(0xD3, add_1reg(0xE0, dreg_hi));
759 /* mov ebx,dreg_lo */
760 EMIT2(0x8B, add_2reg(0xC0, dreg_lo, IA32_EBX));
761 /* shl dreg_lo,cl */ 747 /* shl dreg_lo,cl */
762 EMIT2(0xD3, add_1reg(0xE0, dreg_lo)); 748 EMIT2(0xD3, add_1reg(0xE0, dreg_lo));
763 749
764 /* IA32_ECX = -IA32_ECX + 32 */ 750 /* if ecx >= 32, mov dreg_lo into dreg_hi and clear dreg_lo */
765 /* neg ecx */
766 EMIT2(0xF7, add_1reg(0xD8, IA32_ECX));
767 /* add ecx,32 */
768 EMIT3(0x83, add_1reg(0xC0, IA32_ECX), 32);
769
770 /* shr ebx,cl */
771 EMIT2(0xD3, add_1reg(0xE8, IA32_EBX));
772 /* or dreg_hi,ebx */
773 EMIT2(0x09, add_2reg(0xC0, dreg_hi, IA32_EBX));
774
775 /* goto out; */
776 if (is_imm8(jmp_label(jmp_label3, 2)))
777 EMIT2(0xEB, jmp_label(jmp_label3, 2));
778 else
779 EMIT1_off32(0xE9, jmp_label(jmp_label3, 5));
780
781 /* >= 32 */
782 if (jmp_label1 == -1)
783 jmp_label1 = cnt;
784 751
785 /* cmp ecx,64 */ 752 /* cmp ecx,32 */
786 EMIT3(0x83, add_1reg(0xF8, IA32_ECX), 64); 753 EMIT3(0x83, add_1reg(0xF8, IA32_ECX), 32);
787 /* Jumps when >= 64 */ 754 /* skip the next two instructions (4 bytes) when < 32 */
788 if (is_imm8(jmp_label(jmp_label2, 2))) 755 EMIT2(IA32_JB, 4);
789 EMIT2(IA32_JAE, jmp_label(jmp_label2, 2));
790 else
791 EMIT2_off32(0x0F, IA32_JAE + 0x10, jmp_label(jmp_label2, 6));
792 756
793 /* >= 32 && < 64 */
794 /* sub ecx,32 */
795 EMIT3(0x83, add_1reg(0xE8, IA32_ECX), 32);
796 /* shl dreg_lo,cl */
797 EMIT2(0xD3, add_1reg(0xE0, dreg_lo));
798 /* mov dreg_hi,dreg_lo */ 757 /* mov dreg_hi,dreg_lo */
799 EMIT2(0x89, add_2reg(0xC0, dreg_hi, dreg_lo)); 758 EMIT2(0x89, add_2reg(0xC0, dreg_hi, dreg_lo));
800
801 /* xor dreg_lo,dreg_lo */ 759 /* xor dreg_lo,dreg_lo */
802 EMIT2(0x33, add_2reg(0xC0, dreg_lo, dreg_lo)); 760 EMIT2(0x33, add_2reg(0xC0, dreg_lo, dreg_lo));
803 761
804 /* goto out; */
805 if (is_imm8(jmp_label(jmp_label3, 2)))
806 EMIT2(0xEB, jmp_label(jmp_label3, 2));
807 else
808 EMIT1_off32(0xE9, jmp_label(jmp_label3, 5));
809
810 /* >= 64 */
811 if (jmp_label2 == -1)
812 jmp_label2 = cnt;
813 /* xor dreg_lo,dreg_lo */
814 EMIT2(0x33, add_2reg(0xC0, dreg_lo, dreg_lo));
815 /* xor dreg_hi,dreg_hi */
816 EMIT2(0x33, add_2reg(0xC0, dreg_hi, dreg_hi));
817
818 if (jmp_label3 == -1)
819 jmp_label3 = cnt;
820
821 if (dstk) { 762 if (dstk) {
822 /* mov dword ptr [ebp+off],dreg_lo */ 763 /* mov dword ptr [ebp+off],dreg_lo */
823 EMIT3(0x89, add_2reg(0x40, IA32_EBP, dreg_lo), 764 EMIT3(0x89, add_2reg(0x40, IA32_EBP, dreg_lo),
@@ -836,9 +777,6 @@ static inline void emit_ia32_arsh_r64(const u8 dst[], const u8 src[],
836{ 777{
837 u8 *prog = *pprog; 778 u8 *prog = *pprog;
838 int cnt = 0; 779 int cnt = 0;
839 static int jmp_label1 = -1;
840 static int jmp_label2 = -1;
841 static int jmp_label3 = -1;
842 u8 dreg_lo = dstk ? IA32_EAX : dst_lo; 780 u8 dreg_lo = dstk ? IA32_EAX : dst_lo;
843 u8 dreg_hi = dstk ? IA32_EDX : dst_hi; 781 u8 dreg_hi = dstk ? IA32_EDX : dst_hi;
844 782
@@ -857,78 +795,22 @@ static inline void emit_ia32_arsh_r64(const u8 dst[], const u8 src[],
857 /* mov ecx,src_lo */ 795 /* mov ecx,src_lo */
858 EMIT2(0x8B, add_2reg(0xC0, src_lo, IA32_ECX)); 796 EMIT2(0x8B, add_2reg(0xC0, src_lo, IA32_ECX));
859 797
860 /* cmp ecx,32 */ 798 /* shrd dreg_lo,dreg_hi,cl */
861 EMIT3(0x83, add_1reg(0xF8, IA32_ECX), 32); 799 EMIT3(0x0F, 0xAD, add_2reg(0xC0, dreg_lo, dreg_hi));
862 /* Jumps when >= 32 */ 800 /* sar dreg_hi,cl */
863 if (is_imm8(jmp_label(jmp_label1, 2)))
864 EMIT2(IA32_JAE, jmp_label(jmp_label1, 2));
865 else
866 EMIT2_off32(0x0F, IA32_JAE + 0x10, jmp_label(jmp_label1, 6));
867
868 /* < 32 */
869 /* lshr dreg_lo,cl */
870 EMIT2(0xD3, add_1reg(0xE8, dreg_lo));
871 /* mov ebx,dreg_hi */
872 EMIT2(0x8B, add_2reg(0xC0, dreg_hi, IA32_EBX));
873 /* ashr dreg_hi,cl */
874 EMIT2(0xD3, add_1reg(0xF8, dreg_hi)); 801 EMIT2(0xD3, add_1reg(0xF8, dreg_hi));
875 802
876 /* IA32_ECX = -IA32_ECX + 32 */ 803 /* if ecx >= 32, mov dreg_hi to dreg_lo and set/clear dreg_hi depending on sign */
877 /* neg ecx */
878 EMIT2(0xF7, add_1reg(0xD8, IA32_ECX));
879 /* add ecx,32 */
880 EMIT3(0x83, add_1reg(0xC0, IA32_ECX), 32);
881
882 /* shl ebx,cl */
883 EMIT2(0xD3, add_1reg(0xE0, IA32_EBX));
884 /* or dreg_lo,ebx */
885 EMIT2(0x09, add_2reg(0xC0, dreg_lo, IA32_EBX));
886
887 /* goto out; */
888 if (is_imm8(jmp_label(jmp_label3, 2)))
889 EMIT2(0xEB, jmp_label(jmp_label3, 2));
890 else
891 EMIT1_off32(0xE9, jmp_label(jmp_label3, 5));
892
893 /* >= 32 */
894 if (jmp_label1 == -1)
895 jmp_label1 = cnt;
896 804
897 /* cmp ecx,64 */ 805 /* cmp ecx,32 */
898 EMIT3(0x83, add_1reg(0xF8, IA32_ECX), 64); 806 EMIT3(0x83, add_1reg(0xF8, IA32_ECX), 32);
899 /* Jumps when >= 64 */ 807 /* skip the next two instructions (5 bytes) when < 32 */
900 if (is_imm8(jmp_label(jmp_label2, 2))) 808 EMIT2(IA32_JB, 5);
901 EMIT2(IA32_JAE, jmp_label(jmp_label2, 2));
902 else
903 EMIT2_off32(0x0F, IA32_JAE + 0x10, jmp_label(jmp_label2, 6));
904 809
905 /* >= 32 && < 64 */
906 /* sub ecx,32 */
907 EMIT3(0x83, add_1reg(0xE8, IA32_ECX), 32);
908 /* ashr dreg_hi,cl */
909 EMIT2(0xD3, add_1reg(0xF8, dreg_hi));
910 /* mov dreg_lo,dreg_hi */ 810 /* mov dreg_lo,dreg_hi */
911 EMIT2(0x89, add_2reg(0xC0, dreg_lo, dreg_hi)); 811 EMIT2(0x89, add_2reg(0xC0, dreg_lo, dreg_hi));
912 812 /* sar dreg_hi,31 */
913 /* ashr dreg_hi,imm8 */
914 EMIT3(0xC1, add_1reg(0xF8, dreg_hi), 31);
915
916 /* goto out; */
917 if (is_imm8(jmp_label(jmp_label3, 2)))
918 EMIT2(0xEB, jmp_label(jmp_label3, 2));
919 else
920 EMIT1_off32(0xE9, jmp_label(jmp_label3, 5));
921
922 /* >= 64 */
923 if (jmp_label2 == -1)
924 jmp_label2 = cnt;
925 /* ashr dreg_hi,imm8 */
926 EMIT3(0xC1, add_1reg(0xF8, dreg_hi), 31); 813 EMIT3(0xC1, add_1reg(0xF8, dreg_hi), 31);
927 /* mov dreg_lo,dreg_hi */
928 EMIT2(0x89, add_2reg(0xC0, dreg_lo, dreg_hi));
929
930 if (jmp_label3 == -1)
931 jmp_label3 = cnt;
932 814
933 if (dstk) { 815 if (dstk) {
934 /* mov dword ptr [ebp+off],dreg_lo */ 816 /* mov dword ptr [ebp+off],dreg_lo */
@@ -948,9 +830,6 @@ static inline void emit_ia32_rsh_r64(const u8 dst[], const u8 src[], bool dstk,
948{ 830{
949 u8 *prog = *pprog; 831 u8 *prog = *pprog;
950 int cnt = 0; 832 int cnt = 0;
951 static int jmp_label1 = -1;
952 static int jmp_label2 = -1;
953 static int jmp_label3 = -1;
954 u8 dreg_lo = dstk ? IA32_EAX : dst_lo; 833 u8 dreg_lo = dstk ? IA32_EAX : dst_lo;
955 u8 dreg_hi = dstk ? IA32_EDX : dst_hi; 834 u8 dreg_hi = dstk ? IA32_EDX : dst_hi;
956 835
@@ -969,77 +848,23 @@ static inline void emit_ia32_rsh_r64(const u8 dst[], const u8 src[], bool dstk,
969 /* mov ecx,src_lo */ 848 /* mov ecx,src_lo */
970 EMIT2(0x8B, add_2reg(0xC0, src_lo, IA32_ECX)); 849 EMIT2(0x8B, add_2reg(0xC0, src_lo, IA32_ECX));
971 850
972 /* cmp ecx,32 */ 851 /* shrd dreg_lo,dreg_hi,cl */
973 EMIT3(0x83, add_1reg(0xF8, IA32_ECX), 32); 852 EMIT3(0x0F, 0xAD, add_2reg(0xC0, dreg_lo, dreg_hi));
974 /* Jumps when >= 32 */
975 if (is_imm8(jmp_label(jmp_label1, 2)))
976 EMIT2(IA32_JAE, jmp_label(jmp_label1, 2));
977 else
978 EMIT2_off32(0x0F, IA32_JAE + 0x10, jmp_label(jmp_label1, 6));
979
980 /* < 32 */
981 /* lshr dreg_lo,cl */
982 EMIT2(0xD3, add_1reg(0xE8, dreg_lo));
983 /* mov ebx,dreg_hi */
984 EMIT2(0x8B, add_2reg(0xC0, dreg_hi, IA32_EBX));
985 /* shr dreg_hi,cl */ 853 /* shr dreg_hi,cl */
986 EMIT2(0xD3, add_1reg(0xE8, dreg_hi)); 854 EMIT2(0xD3, add_1reg(0xE8, dreg_hi));
987 855
988 /* IA32_ECX = -IA32_ECX + 32 */ 856 /* if ecx >= 32, mov dreg_hi to dreg_lo and clear dreg_hi */
989 /* neg ecx */
990 EMIT2(0xF7, add_1reg(0xD8, IA32_ECX));
991 /* add ecx,32 */
992 EMIT3(0x83, add_1reg(0xC0, IA32_ECX), 32);
993
994 /* shl ebx,cl */
995 EMIT2(0xD3, add_1reg(0xE0, IA32_EBX));
996 /* or dreg_lo,ebx */
997 EMIT2(0x09, add_2reg(0xC0, dreg_lo, IA32_EBX));
998 857
999 /* goto out; */ 858 /* cmp ecx,32 */
1000 if (is_imm8(jmp_label(jmp_label3, 2))) 859 EMIT3(0x83, add_1reg(0xF8, IA32_ECX), 32);
1001 EMIT2(0xEB, jmp_label(jmp_label3, 2)); 860 /* skip the next two instructions (4 bytes) when < 32 */
1002 else 861 EMIT2(IA32_JB, 4);
1003 EMIT1_off32(0xE9, jmp_label(jmp_label3, 5));
1004
1005 /* >= 32 */
1006 if (jmp_label1 == -1)
1007 jmp_label1 = cnt;
1008 /* cmp ecx,64 */
1009 EMIT3(0x83, add_1reg(0xF8, IA32_ECX), 64);
1010 /* Jumps when >= 64 */
1011 if (is_imm8(jmp_label(jmp_label2, 2)))
1012 EMIT2(IA32_JAE, jmp_label(jmp_label2, 2));
1013 else
1014 EMIT2_off32(0x0F, IA32_JAE + 0x10, jmp_label(jmp_label2, 6));
1015 862
1016 /* >= 32 && < 64 */
1017 /* sub ecx,32 */
1018 EMIT3(0x83, add_1reg(0xE8, IA32_ECX), 32);
1019 /* shr dreg_hi,cl */
1020 EMIT2(0xD3, add_1reg(0xE8, dreg_hi));
1021 /* mov dreg_lo,dreg_hi */ 863 /* mov dreg_lo,dreg_hi */
1022 EMIT2(0x89, add_2reg(0xC0, dreg_lo, dreg_hi)); 864 EMIT2(0x89, add_2reg(0xC0, dreg_lo, dreg_hi));
1023 /* xor dreg_hi,dreg_hi */ 865 /* xor dreg_hi,dreg_hi */
1024 EMIT2(0x33, add_2reg(0xC0, dreg_hi, dreg_hi)); 866 EMIT2(0x33, add_2reg(0xC0, dreg_hi, dreg_hi));
1025 867
1026 /* goto out; */
1027 if (is_imm8(jmp_label(jmp_label3, 2)))
1028 EMIT2(0xEB, jmp_label(jmp_label3, 2));
1029 else
1030 EMIT1_off32(0xE9, jmp_label(jmp_label3, 5));
1031
1032 /* >= 64 */
1033 if (jmp_label2 == -1)
1034 jmp_label2 = cnt;
1035 /* xor dreg_lo,dreg_lo */
1036 EMIT2(0x33, add_2reg(0xC0, dreg_lo, dreg_lo));
1037 /* xor dreg_hi,dreg_hi */
1038 EMIT2(0x33, add_2reg(0xC0, dreg_hi, dreg_hi));
1039
1040 if (jmp_label3 == -1)
1041 jmp_label3 = cnt;
1042
1043 if (dstk) { 868 if (dstk) {
1044 /* mov dword ptr [ebp+off],dreg_lo */ 869 /* mov dword ptr [ebp+off],dreg_lo */
1045 EMIT3(0x89, add_2reg(0x40, IA32_EBP, dreg_lo), 870 EMIT3(0x89, add_2reg(0x40, IA32_EBP, dreg_lo),