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  }