github.com/google/syzkaller@v0.0.0-20251211124644-a066d2bc4b02/prog/decodeexec.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 prog
     5  
     6  import (
     7  	"encoding/binary"
     8  	"fmt"
     9  	"reflect"
    10  	"strings"
    11  )
    12  
    13  type ExecProg struct {
    14  	Calls []ExecCall
    15  	Vars  []uint64
    16  }
    17  
    18  type ExecCall struct {
    19  	Meta    *Syscall
    20  	Props   CallProps
    21  	Index   uint64
    22  	Args    []ExecArg
    23  	Copyin  []ExecCopyin
    24  	Copyout []ExecCopyout
    25  }
    26  
    27  type ExecCopyin struct {
    28  	Addr uint64
    29  	Arg  ExecArg
    30  }
    31  
    32  type ExecCopyout struct {
    33  	Index uint64
    34  	Addr  uint64
    35  	Size  uint64
    36  }
    37  
    38  type ExecArg interface{} // one of ExecArg*
    39  
    40  type ExecArgConst struct {
    41  	Size           uint64
    42  	Format         BinaryFormat
    43  	Value          uint64
    44  	BitfieldOffset uint64
    45  	BitfieldLength uint64
    46  	PidStride      uint64
    47  }
    48  
    49  type ExecArgResult struct {
    50  	Size    uint64
    51  	Format  BinaryFormat
    52  	Index   uint64
    53  	DivOp   uint64
    54  	AddOp   uint64
    55  	Default uint64
    56  }
    57  
    58  type ExecArgData struct {
    59  	Data     []byte
    60  	Readable bool
    61  }
    62  
    63  type ExecArgCsum struct {
    64  	Size   uint64
    65  	Kind   uint64
    66  	Chunks []ExecCsumChunk
    67  }
    68  
    69  type ExecCsumChunk struct {
    70  	Kind  uint64
    71  	Value uint64
    72  	Size  uint64
    73  }
    74  
    75  func ExecCallCount(exec []byte) (int, error) {
    76  	v, n := binary.Varint(exec)
    77  	if n <= 0 {
    78  		return 0, fmt.Errorf("not enough data in the buffer")
    79  	}
    80  	if v > MaxCalls {
    81  		return 0, fmt.Errorf("too many calls (%v)", v)
    82  	}
    83  	return int(v), nil
    84  }
    85  
    86  func (target *Target) DeserializeExec(exec []byte, stats map[string]int) (ExecProg, error) {
    87  	dec := &execDecoder{target: target, data: exec, stats: stats}
    88  	dec.parse()
    89  	if dec.err != nil {
    90  		return ExecProg{}, dec.err
    91  	}
    92  	if uint64(len(dec.vars)) != dec.numVars {
    93  		return ExecProg{}, fmt.Errorf("mismatching number of vars: %v/%v",
    94  			len(dec.vars), dec.numVars)
    95  	}
    96  	p := ExecProg{
    97  		Calls: dec.calls,
    98  		Vars:  dec.vars,
    99  	}
   100  	return p, nil
   101  }
   102  
   103  type execDecoder struct {
   104  	target  *Target
   105  	data    []byte
   106  	err     error
   107  	numVars uint64
   108  	vars    []uint64
   109  	call    ExecCall
   110  	calls   []ExecCall
   111  	stats   map[string]int
   112  }
   113  
   114  func (dec *execDecoder) parse() {
   115  	ncalls := dec.read("header")
   116  	for dec.err == nil {
   117  		switch instr := dec.read("instr/opcode"); instr {
   118  		case execInstrCopyin:
   119  			dec.commitCall()
   120  			dec.call.Copyin = append(dec.call.Copyin, ExecCopyin{
   121  				Addr: dec.read("instr/copyin") + dec.target.DataOffset,
   122  				Arg:  dec.readArg(),
   123  			})
   124  		case execInstrCopyout:
   125  			dec.call.Copyout = append(dec.call.Copyout, ExecCopyout{
   126  				Index: dec.read("instr/copyout/index"),
   127  				Addr:  dec.read("instr/copyout/addr") + dec.target.DataOffset,
   128  				Size:  dec.read("instr/copyout/size"),
   129  			})
   130  		case execInstrEOF:
   131  			dec.commitCall()
   132  			if ncalls != uint64(len(dec.calls)) {
   133  				dec.err = fmt.Errorf("bad number of calls: %v/%v", ncalls, len(dec.calls))
   134  			}
   135  			return
   136  		case execInstrSetProps:
   137  			dec.commitCall()
   138  			dec.readCallProps(&dec.call.Props)
   139  		default:
   140  			dec.commitCall()
   141  			if instr >= uint64(len(dec.target.Syscalls)) {
   142  				dec.setErr(fmt.Errorf("bad syscall %v", instr))
   143  				return
   144  			}
   145  			dec.call.Meta = dec.target.Syscalls[instr]
   146  			dec.call.Index = dec.read("instr/index")
   147  			for i := dec.read("instr/nargs"); i > 0; i-- {
   148  				switch arg := dec.readArg(); arg.(type) {
   149  				case ExecArgConst, ExecArgResult:
   150  					dec.call.Args = append(dec.call.Args, arg)
   151  				default:
   152  					dec.setErr(fmt.Errorf("bad call arg %+v", arg))
   153  					return
   154  				}
   155  			}
   156  		}
   157  	}
   158  }
   159  
   160  func (dec *execDecoder) readCallProps(props *CallProps) {
   161  	props.ForeachProp(func(_, _ string, value reflect.Value) {
   162  		arg := dec.read("call prop")
   163  		switch kind := value.Kind(); kind {
   164  		case reflect.Int:
   165  			value.SetInt(int64(arg))
   166  		case reflect.Bool:
   167  			if arg == 1 {
   168  				value.SetBool(true)
   169  			}
   170  		default:
   171  			panic("Unsupported (yet) kind: " + kind.String())
   172  		}
   173  	})
   174  }
   175  
   176  func (dec *execDecoder) readArg() ExecArg {
   177  	switch typ := dec.read("arg/type"); typ {
   178  	case execArgConst:
   179  		meta := dec.read("arg/const/meta")
   180  		return ExecArgConst{
   181  			Value:          dec.read("arg/const/value"),
   182  			Size:           meta & 0xff,
   183  			Format:         BinaryFormat((meta >> 8) & 0xff),
   184  			BitfieldOffset: (meta >> 16) & 0xff,
   185  			BitfieldLength: (meta >> 24) & 0xff,
   186  			PidStride:      meta >> 32,
   187  		}
   188  	case execArgAddr32:
   189  		fallthrough
   190  	case execArgAddr64:
   191  		size := 4
   192  		if typ == execArgAddr64 {
   193  			size = 8
   194  		}
   195  		return ExecArgConst{
   196  			Value: dec.read("arg/addr") + dec.target.DataOffset,
   197  			Size:  uint64(size),
   198  		}
   199  	case execArgResult:
   200  		meta := dec.read("arg/result/meta")
   201  		arg := ExecArgResult{
   202  			Size:    meta & 0xff,
   203  			Format:  BinaryFormat((meta >> 8) & 0xff),
   204  			Index:   dec.read("arg/result/index"),
   205  			DivOp:   dec.read("arg/result/divop"),
   206  			AddOp:   dec.read("arg/result/addop"),
   207  			Default: dec.read("arg/result/default"),
   208  		}
   209  		for uint64(len(dec.vars)) <= arg.Index {
   210  			dec.vars = append(dec.vars, 0)
   211  		}
   212  		dec.vars[arg.Index] = arg.Default
   213  		return arg
   214  	case execArgData:
   215  		flags := dec.read("arg/data/size")
   216  		size := flags & ^execArgDataReadable
   217  		dec.addStat("arg/data/blob", int(size))
   218  		readable := flags&execArgDataReadable != 0
   219  		return ExecArgData{
   220  			Data:     dec.readBlob(size),
   221  			Readable: readable,
   222  		}
   223  	case execArgCsum:
   224  		size := dec.read("arg/csum/size")
   225  		switch kind := dec.read("arg/csum/kind"); kind {
   226  		case ExecArgCsumInet:
   227  			chunks := make([]ExecCsumChunk, dec.read("arg/csum/chunks"))
   228  			for i := range chunks {
   229  				kind := dec.read("arg/csum/chunk/kind")
   230  				addr := dec.read("arg/csum/chunk/addr")
   231  				size := dec.read("arg/csum/chunk/size")
   232  				if kind == ExecArgCsumChunkData {
   233  					addr += dec.target.DataOffset
   234  				}
   235  				chunks[i] = ExecCsumChunk{
   236  					Kind:  kind,
   237  					Value: addr,
   238  					Size:  size,
   239  				}
   240  			}
   241  			return ExecArgCsum{
   242  				Size:   size,
   243  				Kind:   kind,
   244  				Chunks: chunks,
   245  			}
   246  		default:
   247  			dec.setErr(fmt.Errorf("unknown csum kind %v", kind))
   248  			return nil
   249  		}
   250  	default:
   251  		dec.setErr(fmt.Errorf("bad argument type %v", typ))
   252  		return nil
   253  	}
   254  }
   255  
   256  func (dec *execDecoder) read(stat string) uint64 {
   257  	if dec.err != nil {
   258  		return 0
   259  	}
   260  	v, n := binary.Varint(dec.data)
   261  	if n <= 0 {
   262  		dec.setErr(fmt.Errorf("exec program overflow"))
   263  		return 0
   264  	}
   265  	dec.addStat(stat, n)
   266  	dec.data = dec.data[n:]
   267  	return uint64(v)
   268  }
   269  
   270  func (dec *execDecoder) readBlob(size uint64) []byte {
   271  	if uint64(len(dec.data)) < size {
   272  		dec.setErr(fmt.Errorf("exec program overflow"))
   273  	}
   274  	if dec.err != nil {
   275  		return nil
   276  	}
   277  	data := dec.data[:size]
   278  	dec.data = dec.data[size:]
   279  	return data
   280  }
   281  
   282  func (dec *execDecoder) setErr(err error) {
   283  	if dec.err == nil {
   284  		dec.err = err
   285  	}
   286  }
   287  
   288  func (dec *execDecoder) commitCall() {
   289  	if dec.call.Meta == nil {
   290  		return
   291  	}
   292  	if dec.call.Index != ExecNoCopyout && dec.numVars < dec.call.Index+1 {
   293  		dec.numVars = dec.call.Index + 1
   294  	}
   295  	for _, copyout := range dec.call.Copyout {
   296  		dec.numVars = max(dec.numVars, copyout.Index+1)
   297  	}
   298  	dec.calls = append(dec.calls, dec.call)
   299  	dec.call = ExecCall{}
   300  }
   301  
   302  func (dec *execDecoder) addStat(stat string, n int) {
   303  	if dec.stats == nil {
   304  		return
   305  	}
   306  	prefix := ""
   307  	for _, part := range strings.Split(stat, "/") {
   308  		dec.stats[prefix+part] += n
   309  		prefix += part + "/"
   310  	}
   311  }