// Copyright 2017 syzkaller project authors. All rights reserved. // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. package ifuzz import ( "fmt" ) // Decode decodes instruction length for the given mode. // It can have falsely decode incorrect instructions, // but should not fail to decode correct instructions. // nolint: gocyclo func Decode(mode int, text []byte) (int, error) { if len(text) == 0 { return 0, fmt.Errorf("zero-length instruction") } prefixes := prefixes32 var operSize, immSize, dispSize, addrSize int switch mode { case ModeLong64: operSize, immSize, dispSize, addrSize = 4, 4, 4, 8 prefixes = prefixes64 case ModeProt32: operSize, immSize, dispSize, addrSize = 4, 4, 4, 4 case ModeProt16, ModeReal16: operSize, immSize, dispSize, addrSize = 2, 2, 2, 2 default: panic("bad mode") } prefixLen := 0 var decodedPrefixes []byte vex := false if len(text) > 1 { // There are only 2 32-bit instructions that look like VEX-prefixed but are actually not: LDS, LES. // They always reference memory (mod!=3), but all VEX instructions have "mod=3" where LDS/LES would have mod. if (text[0] == 0xc4 || text[0] == 0xc5) && (mode == ModeLong64 || text[1]&0xc0 == 0xc0) { vex = true } // There is only one instruction that looks like XOP-prefixed but is actually not: POP. // It always has reg=0, but all XOP instructions have "reg!=0" where POP would have reg. if text[0] == 0x8f && text[1]&0x38 != 0 { vex = true } } var vexMap byte if vex { prefixLen = 3 if text[0] == 0xc5 { prefixLen = 2 vexMap = 1 // V0F } if len(text) < prefixLen { return 0, fmt.Errorf("bad VEX/XOP prefix") } if prefixLen == 3 { vexMap = text[1] & 0x1f } text = text[prefixLen:] } else { decodedPrefixes = text operSize1, immSize1, dispSize1, addrSize1 := operSize, immSize, dispSize, addrSize for len(text) != 0 && prefixes[text[0]] { switch text[0] { case 0x66: if immSize == 4 { immSize1 = 2 operSize1 = 2 } else if immSize == 2 { immSize1 = 4 operSize1 = 4 } case 0x67: if addrSize == 8 { addrSize1 = 4 } else if addrSize == 4 { dispSize1 = 2 addrSize1 = 2 } else if addrSize == 2 { dispSize1 = 4 addrSize1 = 4 } } if text[0] & ^byte(7) == 0x48 { operSize1 = 8 immSize1 = 4 } text = text[1:] prefixLen++ } operSize, immSize, dispSize, addrSize = operSize1, immSize1, dispSize1, addrSize1 decodedPrefixes = decodedPrefixes[:prefixLen] if len(text) == 0 { return 0, fmt.Errorf("no opcode, only prefixes") } } nextInsn: for _, insn := range modeInsns[mode][typeAll] { if vex != (insn.Vex != 0) { continue nextInsn } if vex && insn.VexMap != vexMap { continue nextInsn } if insn.NoRepPrefix || insn.No66Prefix { for _, p := range decodedPrefixes { if len(insn.Prefix) != 0 && insn.Prefix[0] == p { continue } switch p { case 0xf2, 0xf3: if insn.NoRepPrefix { continue nextInsn } case 0x66: if insn.No66Prefix { continue nextInsn } } } } text1 := text for i, v := range insn.Opcode { if len(text1) == 0 { continue nextInsn } b := text1[0] if insn.Srm && i == len(insn.Opcode)-1 { b &^= 7 } if b != v { continue nextInsn } text1 = text1[1:] } if insn.Modrm { if len(text1) == 0 { continue nextInsn } modrm := text1[0] text1 = text1[1:] mod := modrm >> 6 rm := modrm & 7 if !insn.NoSibDisp { disp := 0 if addrSize == 2 { if mod == 1 { disp = 1 } else if mod == 2 || mod == 0 && rm == 6 { disp = 2 } } else { var sibbase byte if mod != 3 && rm == 4 { if len(text1) == 0 { continue nextInsn } sib := text1[0] text1 = text1[1:] sibbase = sib & 7 } if mod == 1 { disp = 1 } else if mod == 2 || mod == 0 && rm == 5 || mod == 0 && sibbase == 5 { disp = dispSize } } if disp != 0 { if len(text1) < disp { continue nextInsn } text1 = text1[disp:] } } } immLen := 0 for _, imm := range []int8{insn.Imm, insn.Imm2} { switch imm { case -1: immLen += immSize case -2: immLen += addrSize case -3: immLen += operSize default: immLen += int(imm) } } if immLen != 0 { if len(text1) < immLen { continue nextInsn } text1 = text1[immLen:] } for _, v := range insn.Suffix { if len(text1) == 0 || text1[0] != v { continue nextInsn } text1 = text1[1:] } return prefixLen + len(text) - len(text1), nil } return 0, fmt.Errorf("unknown instruction") } var XedDecode func(mode int, text []byte) (int, error) var ( prefixes32 = map[byte]bool{ 0x2E: true, 0x3E: true, 0x26: true, 0x64: true, 0x65: true, 0x36: true, 0x66: true, 0x67: true, 0xF3: true, 0xF2: true, 0xF0: true, } prefixes64 = map[byte]bool{ 0x2E: true, 0x3E: true, 0x26: true, 0x64: true, 0x65: true, 0x36: true, 0x66: true, 0x67: true, 0xF3: true, 0xF2: true, 0xF0: true, 0x40: true, 0x41: true, 0x42: true, 0x43: true, 0x44: true, 0x45: true, 0x46: true, 0x47: true, 0x48: true, 0x49: true, 0x4a: true, 0x4b: true, 0x4c: true, 0x4d: true, 0x4e: true, 0x4f: true, } )