diff options
author | Luke Nelson <lukenels@cs.washington.edu> | 2019-06-29 01:57:49 -0400 |
---|---|---|
committer | Daniel Borkmann <daniel@iogearbox.net> | 2019-07-03 05:14:28 -0400 |
commit | 68a8357ec15bdce55266e9fba8b8b3b8143fa7d2 (patch) | |
tree | 2a00d404a186fdc829f496f988df13b7fefc7a6b | |
parent | 0472301a28f6cf53a6bc5783e48a2d0bbff4682f (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.c | 221 |
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), |