syzkaller/pkg/ifuzz/decode.go
Dmitry Vyukov 2c7e14a847 gometalinter: enable cyclomatic complexity checking
Refactor some functions to be simpler.

Update #538
2018-05-04 18:03:46 +02:00

223 lines
5.2 KiB
Go

// 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,
}
)