github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/pkg/ifuzz/x86/decode.go (about) 1 // Copyright 2017 syzkaller project authors. All rights reserved. 2 // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 4 package x86 5 6 import ( 7 "fmt" 8 9 "github.com/google/syzkaller/pkg/ifuzz/iset" 10 ) 11 12 // Decode decodes instruction length for the given mode. 13 // It can have falsely decode incorrect instructions, 14 // but should not fail to decode correct instructions. 15 // nolint: gocyclo, nestif, gocognit, funlen 16 func (insnset *InsnSet) Decode(mode iset.Mode, text []byte) (int, error) { 17 if len(text) == 0 { 18 return 0, fmt.Errorf("zero-length instruction") 19 } 20 prefixes := prefixes32 21 var operSize, immSize, dispSize, addrSize int 22 switch mode { 23 case iset.ModeLong64: 24 operSize, immSize, dispSize, addrSize = 4, 4, 4, 8 25 prefixes = prefixes64 26 case iset.ModeProt32: 27 operSize, immSize, dispSize, addrSize = 4, 4, 4, 4 28 case iset.ModeProt16, iset.ModeReal16: 29 operSize, immSize, dispSize, addrSize = 2, 2, 2, 2 30 default: 31 panic("bad mode") 32 } 33 prefixLen := 0 34 var decodedPrefixes []byte 35 vex := false 36 if len(text) > 1 { 37 // There are only 2 32-bit instructions that look like VEX-prefixed but are actually not: LDS, LES. 38 // They always reference memory (mod!=3), but all VEX instructions have "mod=3" where LDS/LES would have mod. 39 if (text[0] == 0xc4 || text[0] == 0xc5) && (mode == iset.ModeLong64 || text[1]&0xc0 == 0xc0) { 40 vex = true 41 } 42 // There is only one instruction that looks like XOP-prefixed but is actually not: POP. 43 // It always has reg=0, but all XOP instructions have "reg!=0" where POP would have reg. 44 if text[0] == 0x8f && text[1]&0x38 != 0 { 45 vex = true 46 } 47 } 48 var vexMap byte 49 if vex { 50 prefixLen = 3 51 if text[0] == 0xc5 { 52 prefixLen = 2 53 vexMap = 1 // V0F 54 } 55 if len(text) < prefixLen { 56 return 0, fmt.Errorf("bad VEX/XOP prefix") 57 } 58 if prefixLen == 3 { 59 vexMap = text[1] & 0x1f 60 } 61 text = text[prefixLen:] 62 } else { 63 decodedPrefixes = text 64 operSize1, immSize1, dispSize1, addrSize1 := operSize, immSize, dispSize, addrSize 65 for len(text) != 0 && prefixes[text[0]] { 66 switch text[0] { 67 case 0x66: 68 if immSize == 4 { 69 immSize1 = 2 70 operSize1 = 2 71 } else if immSize == 2 { 72 immSize1 = 4 73 operSize1 = 4 74 } 75 case 0x67: 76 if addrSize == 8 { 77 addrSize1 = 4 78 } else if addrSize == 4 { 79 dispSize1 = 2 80 addrSize1 = 2 81 } else if addrSize == 2 { 82 dispSize1 = 4 83 addrSize1 = 4 84 } 85 } 86 if text[0] & ^byte(7) == 0x48 { 87 operSize1 = 8 88 immSize1 = 4 89 } 90 text = text[1:] 91 prefixLen++ 92 } 93 operSize, immSize, dispSize, addrSize = operSize1, immSize1, dispSize1, addrSize1 94 decodedPrefixes = decodedPrefixes[:prefixLen] 95 if len(text) == 0 { 96 return 0, fmt.Errorf("no opcode, only prefixes") 97 } 98 } 99 nextInsn: 100 for _, insn := range insnset.Insns { 101 if (insn.Mode & (1 << mode)) == 0 { 102 continue nextInsn 103 } 104 if vex != (insn.Vex != 0) { 105 continue nextInsn 106 } 107 if vex && insn.VexMap != vexMap { 108 continue nextInsn 109 } 110 if insn.NoRepPrefix || insn.No66Prefix { 111 for _, p := range decodedPrefixes { 112 if len(insn.Prefix) != 0 && insn.Prefix[0] == p { 113 continue 114 } 115 switch p { 116 case 0xf2, 0xf3: 117 if insn.NoRepPrefix { 118 continue nextInsn 119 } 120 case 0x66: 121 if insn.No66Prefix { 122 continue nextInsn 123 } 124 } 125 } 126 } 127 text1 := text 128 for i, v := range insn.Opcode { 129 if len(text1) == 0 { 130 continue nextInsn 131 } 132 b := text1[0] 133 if insn.Srm && i == len(insn.Opcode)-1 { 134 b &^= 7 135 } 136 if b != v { 137 continue nextInsn 138 } 139 text1 = text1[1:] 140 } 141 if insn.Modrm { 142 if len(text1) == 0 { 143 continue nextInsn 144 } 145 modrm := text1[0] 146 text1 = text1[1:] 147 mod := modrm >> 6 148 reg := int8(modrm>>3) & 7 149 rm := modrm & 7 150 if insn.Reg >= 0 && reg != insn.Reg { 151 continue nextInsn 152 } 153 if !insn.NoSibDisp { 154 disp := 0 155 if addrSize == 2 { 156 if mod == 1 { 157 disp = 1 158 } else if mod == 2 || mod == 0 && rm == 6 { 159 disp = 2 160 } 161 } else { 162 var sibbase byte 163 if mod != 3 && rm == 4 { 164 if len(text1) == 0 { 165 continue nextInsn 166 } 167 sib := text1[0] 168 text1 = text1[1:] 169 sibbase = sib & 7 170 } 171 if mod == 1 { 172 disp = 1 173 } else if mod == 2 || mod == 0 && rm == 5 || mod == 0 && sibbase == 5 { 174 disp = dispSize 175 } 176 } 177 if disp != 0 { 178 if len(text1) < disp { 179 continue nextInsn 180 } 181 text1 = text1[disp:] 182 } 183 } 184 } 185 immLen := 0 186 for _, imm := range []int8{insn.Imm, insn.Imm2} { 187 switch imm { 188 case -1: 189 immLen += immSize 190 case -2: 191 immLen += addrSize 192 case -3: 193 immLen += operSize 194 default: 195 immLen += int(imm) 196 } 197 } 198 if immLen != 0 { 199 if len(text1) < immLen { 200 continue nextInsn 201 } 202 text1 = text1[immLen:] 203 } 204 for _, v := range insn.Suffix { 205 if len(text1) == 0 || text1[0] != v { 206 continue nextInsn 207 } 208 text1 = text1[1:] 209 } 210 return prefixLen + len(text) - len(text1), nil 211 } 212 return 0, fmt.Errorf("unknown instruction") 213 } 214 215 var XedDecode func(mode iset.Mode, text []byte) (int, error) 216 217 var ( 218 prefixes32 = map[byte]bool{ 219 0x2E: true, 0x3E: true, 0x26: true, 0x64: true, 0x65: true, 220 0x36: true, 0x66: true, 0x67: true, 0xF3: true, 0xF2: true, 221 0xF0: true, 222 } 223 prefixes64 = map[byte]bool{ 224 0x2E: true, 0x3E: true, 0x26: true, 0x64: true, 0x65: true, 225 0x36: true, 0x66: true, 0x67: true, 0xF3: true, 0xF2: true, 226 0xF0: true, 0x40: true, 0x41: true, 0x42: true, 0x43: true, 227 0x44: true, 0x45: true, 0x46: true, 0x47: true, 0x48: true, 228 0x49: true, 0x4a: true, 0x4b: true, 0x4c: true, 0x4d: true, 229 0x4e: true, 0x4f: true, 230 } 231 ) 232 233 func (insnset *InsnSet) DecodeExt(mode iset.Mode, text []byte) (int, error) { 234 if XedDecode != nil && text != nil && len(text) > 0 { 235 return XedDecode(mode, text) 236 } 237 if XedDecode == nil { 238 return 0, fmt.Errorf("no XED") 239 } 240 return 0, nil // tells the caller XED is enabled 241 }