github.com/google/syzkaller@v0.0.0-20251211124644-a066d2bc4b02/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 switch immSize { 69 case 4: 70 immSize1 = 2 71 operSize1 = 2 72 case 2: 73 immSize1 = 4 74 operSize1 = 4 75 } 76 case 0x67: 77 switch addrSize { 78 case 8: 79 addrSize1 = 4 80 case 4: 81 dispSize1 = 2 82 addrSize1 = 2 83 case 2: 84 dispSize1 = 4 85 addrSize1 = 4 86 } 87 } 88 if text[0] & ^byte(7) == 0x48 { 89 operSize1 = 8 90 immSize1 = 4 91 } 92 text = text[1:] 93 prefixLen++ 94 } 95 operSize, immSize, dispSize, addrSize = operSize1, immSize1, dispSize1, addrSize1 96 decodedPrefixes = decodedPrefixes[:prefixLen] 97 if len(text) == 0 { 98 return 0, fmt.Errorf("no opcode, only prefixes") 99 } 100 } 101 nextInsn: 102 for _, insn := range insnset.Insns { 103 if (insn.Mode & (1 << mode)) == 0 { 104 continue nextInsn 105 } 106 if vex != (insn.Vex != 0) { 107 continue nextInsn 108 } 109 if vex && insn.VexMap != vexMap { 110 continue nextInsn 111 } 112 if insn.NoRepPrefix || insn.No66Prefix { 113 for _, p := range decodedPrefixes { 114 if len(insn.Prefix) != 0 && insn.Prefix[0] == p { 115 continue 116 } 117 switch p { 118 case 0xf2, 0xf3: 119 if insn.NoRepPrefix { 120 continue nextInsn 121 } 122 case 0x66: 123 if insn.No66Prefix { 124 continue nextInsn 125 } 126 } 127 } 128 } 129 text1 := text 130 for i, v := range insn.Opcode { 131 if len(text1) == 0 { 132 continue nextInsn 133 } 134 b := text1[0] 135 if insn.Srm && i == len(insn.Opcode)-1 { 136 b &^= 7 137 } 138 if b != v { 139 continue nextInsn 140 } 141 text1 = text1[1:] 142 } 143 if insn.Modrm { 144 if len(text1) == 0 { 145 continue nextInsn 146 } 147 modrm := text1[0] 148 text1 = text1[1:] 149 mod := modrm >> 6 150 reg := int8(modrm>>3) & 7 151 rm := modrm & 7 152 if insn.Reg >= 0 && reg != insn.Reg { 153 continue nextInsn 154 } 155 if !insn.NoSibDisp { 156 disp := 0 157 if addrSize == 2 { 158 if mod == 1 { 159 disp = 1 160 } else if mod == 2 || mod == 0 && rm == 6 { 161 disp = 2 162 } 163 } else { 164 var sibbase byte 165 if mod != 3 && rm == 4 { 166 if len(text1) == 0 { 167 continue nextInsn 168 } 169 sib := text1[0] 170 text1 = text1[1:] 171 sibbase = sib & 7 172 } 173 if mod == 1 { 174 disp = 1 175 } else if mod == 2 || mod == 0 && rm == 5 || mod == 0 && sibbase == 5 { 176 disp = dispSize 177 } 178 } 179 if disp != 0 { 180 if len(text1) < disp { 181 continue nextInsn 182 } 183 text1 = text1[disp:] 184 } 185 } 186 } 187 immLen := 0 188 for _, imm := range []int8{insn.Imm, insn.Imm2} { 189 switch imm { 190 case -1: 191 immLen += immSize 192 case -2: 193 immLen += addrSize 194 case -3: 195 immLen += operSize 196 default: 197 immLen += int(imm) 198 } 199 } 200 if immLen != 0 { 201 if len(text1) < immLen { 202 continue nextInsn 203 } 204 text1 = text1[immLen:] 205 } 206 for _, v := range insn.Suffix { 207 if len(text1) == 0 || text1[0] != v { 208 continue nextInsn 209 } 210 text1 = text1[1:] 211 } 212 return prefixLen + len(text) - len(text1), nil 213 } 214 return 0, fmt.Errorf("unknown instruction") 215 } 216 217 var XedDecode func(mode iset.Mode, text []byte) (int, error) 218 219 var ( 220 prefixes32 = map[byte]bool{ 221 0x2E: true, 0x3E: true, 0x26: true, 0x64: true, 0x65: true, 222 0x36: true, 0x66: true, 0x67: true, 0xF3: true, 0xF2: true, 223 0xF0: true, 224 } 225 prefixes64 = map[byte]bool{ 226 0x2E: true, 0x3E: true, 0x26: true, 0x64: true, 0x65: true, 227 0x36: true, 0x66: true, 0x67: true, 0xF3: true, 0xF2: true, 228 0xF0: true, 0x40: true, 0x41: true, 0x42: true, 0x43: true, 229 0x44: true, 0x45: true, 0x46: true, 0x47: true, 0x48: true, 230 0x49: true, 0x4a: true, 0x4b: true, 0x4c: true, 0x4d: true, 231 0x4e: true, 0x4f: true, 232 } 233 ) 234 235 func (insnset *InsnSet) DecodeExt(mode iset.Mode, text []byte) (int, error) { 236 if XedDecode != nil && text != nil && len(text) > 0 { 237 return XedDecode(mode, text) 238 } 239 if XedDecode == nil { 240 return 0, fmt.Errorf("no XED") 241 } 242 return 0, nil // tells the caller XED is enabled 243 }