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  }