github.com/google/syzkaller@v0.0.0-20251211124644-a066d2bc4b02/prog/prog.go (about)

     1  // Copyright 2015 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  	"fmt"
     8  	"reflect"
     9  	"strings"
    10  )
    11  
    12  type Prog struct {
    13  	Target   *Target
    14  	Calls    []*Call
    15  	Comments []string
    16  
    17  	// Was deserialized using Unsafe mode, so can do unsafe things.
    18  	isUnsafe bool
    19  }
    20  
    21  const ExtraCallName = ".extra"
    22  
    23  func (p *Prog) CallName(call int) string {
    24  	if call >= len(p.Calls) || call < -1 {
    25  		panic(fmt.Sprintf("bad call index %v/%v", call, len(p.Calls)))
    26  	}
    27  	if call == -1 {
    28  		return ExtraCallName
    29  	}
    30  	return p.Calls[call].Meta.Name
    31  }
    32  
    33  // OnlyContains determines whether the program only consists of the syscalls from the first argument.
    34  func (p *Prog) OnlyContains(syscalls map[*Syscall]bool) bool {
    35  	for _, c := range p.Calls {
    36  		if !syscalls[c.Meta] {
    37  			return false
    38  		}
    39  	}
    40  	return true
    41  }
    42  
    43  // FilterInplace only leaves the allowed system calls and deletes all remaining ones.
    44  func (p *Prog) FilterInplace(allowed map[*Syscall]bool) {
    45  	for i := 0; i < len(p.Calls); {
    46  		c := p.Calls[i]
    47  		if !allowed[c.Meta] {
    48  			p.RemoveCall(i)
    49  			continue
    50  		}
    51  		i++
    52  	}
    53  }
    54  
    55  // These properties are parsed and serialized according to the tag and the type
    56  // of the corresponding fields.
    57  // IMPORTANT: keep the exact values of "key" tag for existing props unchanged,
    58  // otherwise the backwards compatibility would be broken.
    59  type CallProps struct {
    60  	FailNth int  `key:"fail_nth"`
    61  	Async   bool `key:"async"`
    62  	Rerun   int  `key:"rerun"`
    63  }
    64  
    65  type Call struct {
    66  	Meta    *Syscall
    67  	Args    []Arg
    68  	Ret     *ResultArg
    69  	Props   CallProps
    70  	Comment string
    71  }
    72  
    73  func MakeCall(meta *Syscall, args []Arg) *Call {
    74  	return &Call{
    75  		Meta: meta,
    76  		Args: args,
    77  		Ret:  MakeReturnArg(meta.Ret),
    78  	}
    79  }
    80  
    81  type Arg interface {
    82  	Type() Type
    83  	Dir() Dir
    84  	Size() uint64
    85  
    86  	validate(ctx *validCtx, dir Dir) error
    87  	serialize(ctx *serializer)
    88  }
    89  
    90  type ArgCommon struct {
    91  	ref Ref
    92  	dir Dir
    93  }
    94  
    95  func (arg *ArgCommon) Type() Type {
    96  	if arg.ref == 0 {
    97  		panic("broken type ref")
    98  	}
    99  	return typeRefs.Load().([]Type)[arg.ref]
   100  }
   101  
   102  func (arg *ArgCommon) Dir() Dir {
   103  	return arg.dir
   104  }
   105  
   106  // Used for ConstType, IntType, FlagsType, LenType, ProcType and CsumType.
   107  type ConstArg struct {
   108  	ArgCommon
   109  	Val uint64
   110  }
   111  
   112  func MakeConstArg(t Type, dir Dir, v uint64) *ConstArg {
   113  	return &ConstArg{ArgCommon: ArgCommon{ref: t.ref(), dir: dir}, Val: v}
   114  }
   115  
   116  func (arg *ConstArg) Size() uint64 {
   117  	return arg.Type().Size()
   118  }
   119  
   120  // Value returns value and pid stride.
   121  func (arg *ConstArg) Value() (uint64, uint64) {
   122  	switch typ := (*arg).Type().(type) {
   123  	case *IntType:
   124  		return arg.Val, 0
   125  	case *ConstType:
   126  		return arg.Val, 0
   127  	case *FlagsType:
   128  		return arg.Val, 0
   129  	case *LenType:
   130  		return arg.Val, 0
   131  	case *ResourceType:
   132  		return arg.Val, 0
   133  	case *CsumType:
   134  		// Checksums are computed dynamically in executor.
   135  		return 0, 0
   136  	case *ProcType:
   137  		if arg.Val == procDefaultValue {
   138  			return 0, 0
   139  		}
   140  		return typ.ValuesStart + arg.Val, typ.ValuesPerProc
   141  	default:
   142  		panic(fmt.Sprintf("unknown ConstArg type %#v", typ))
   143  	}
   144  }
   145  
   146  // Used for PtrType and VmaType.
   147  type PointerArg struct {
   148  	ArgCommon
   149  	Address uint64
   150  	VmaSize uint64 // size of the referenced region for vma args
   151  	Res     Arg    // pointee (nil for vma)
   152  }
   153  
   154  func MakePointerArg(t Type, dir Dir, addr uint64, data Arg) *PointerArg {
   155  	if data == nil {
   156  		panic("nil pointer data arg")
   157  	}
   158  	return &PointerArg{
   159  		ArgCommon: ArgCommon{ref: t.ref(), dir: DirIn}, // pointers are always in
   160  		Address:   addr,
   161  		Res:       data,
   162  	}
   163  }
   164  
   165  func MakeVmaPointerArg(t Type, dir Dir, addr, size uint64) *PointerArg {
   166  	if addr%1024 != 0 {
   167  		panic("unaligned vma address")
   168  	}
   169  	return &PointerArg{
   170  		ArgCommon: ArgCommon{ref: t.ref(), dir: dir},
   171  		Address:   addr,
   172  		VmaSize:   size,
   173  	}
   174  }
   175  
   176  func MakeSpecialPointerArg(t Type, dir Dir, index uint64) *PointerArg {
   177  	if index >= maxSpecialPointers {
   178  		panic("bad special pointer index")
   179  	}
   180  	if _, ok := t.(*PtrType); ok {
   181  		dir = DirIn // pointers are always in
   182  	}
   183  	return &PointerArg{
   184  		ArgCommon: ArgCommon{ref: t.ref(), dir: dir},
   185  		Address:   -index,
   186  	}
   187  }
   188  
   189  func (arg *PointerArg) Size() uint64 {
   190  	return arg.Type().Size()
   191  }
   192  
   193  func (arg *PointerArg) IsSpecial() bool {
   194  	return arg.VmaSize == 0 && arg.Res == nil && -arg.Address < maxSpecialPointers
   195  }
   196  
   197  func (target *Target) PhysicalAddr(arg *PointerArg) uint64 {
   198  	if arg.IsSpecial() {
   199  		return target.SpecialPointers[-arg.Address]
   200  	}
   201  	return target.DataOffset + arg.Address
   202  }
   203  
   204  // Used for BufferType.
   205  type DataArg struct {
   206  	ArgCommon
   207  	data []byte // for in/inout args
   208  	size uint64 // for out Args
   209  }
   210  
   211  func MakeDataArg(t Type, dir Dir, data []byte) *DataArg {
   212  	if dir == DirOut {
   213  		panic("non-empty output data arg")
   214  	}
   215  	return &DataArg{ArgCommon: ArgCommon{ref: t.ref(), dir: dir}, data: append([]byte{}, data...)}
   216  }
   217  
   218  func MakeOutDataArg(t Type, dir Dir, size uint64) *DataArg {
   219  	if dir != DirOut {
   220  		panic("empty input data arg")
   221  	}
   222  	return &DataArg{ArgCommon: ArgCommon{ref: t.ref(), dir: dir}, size: size}
   223  }
   224  
   225  func (arg *DataArg) Size() uint64 {
   226  	if len(arg.data) != 0 {
   227  		return uint64(len(arg.data))
   228  	}
   229  	return arg.size
   230  }
   231  
   232  func (arg *DataArg) Data() []byte {
   233  	if arg.Dir() == DirOut {
   234  		panic("getting data of output data arg")
   235  	}
   236  	return arg.data
   237  }
   238  
   239  func (arg *DataArg) SetData(data []byte) {
   240  	if arg.Dir() == DirOut {
   241  		panic("setting data of output data arg")
   242  	}
   243  	arg.data = append([]byte{}, data...)
   244  }
   245  
   246  // Used for StructType and ArrayType.
   247  // Logical group of args (struct or array).
   248  type GroupArg struct {
   249  	ArgCommon
   250  	Inner []Arg
   251  }
   252  
   253  func MakeGroupArg(t Type, dir Dir, inner []Arg) *GroupArg {
   254  	return &GroupArg{ArgCommon: ArgCommon{ref: t.ref(), dir: dir}, Inner: inner}
   255  }
   256  
   257  func (arg *GroupArg) Size() uint64 {
   258  	typ0 := arg.Type()
   259  	if !typ0.Varlen() {
   260  		return typ0.Size()
   261  	}
   262  	switch typ := typ0.(type) {
   263  	case *StructType:
   264  		var size, offset uint64
   265  		for i, fld := range arg.Inner {
   266  			if i == typ.OverlayField {
   267  				offset = 0
   268  			}
   269  			offset += fld.Size()
   270  			// Add dynamic alignment at the end and before the overlay part.
   271  			if i+1 == len(arg.Inner) || i+1 == typ.OverlayField {
   272  				if typ.AlignAttr != 0 && offset%typ.AlignAttr != 0 {
   273  					offset += typ.AlignAttr - offset%typ.AlignAttr
   274  				}
   275  			}
   276  			size = max(size, offset)
   277  		}
   278  		return size
   279  	case *ArrayType:
   280  		var size uint64
   281  		for _, elem := range arg.Inner {
   282  			size += elem.Size()
   283  		}
   284  		return size
   285  	default:
   286  		panic(fmt.Sprintf("bad group arg type %v", typ))
   287  	}
   288  }
   289  
   290  func (arg *GroupArg) fixedInnerSize() bool {
   291  	switch typ := arg.Type().(type) {
   292  	case *StructType:
   293  		return true
   294  	case *ArrayType:
   295  		return typ.Kind == ArrayRangeLen && typ.RangeBegin == typ.RangeEnd
   296  	default:
   297  		panic(fmt.Sprintf("bad group arg type %v", typ))
   298  	}
   299  }
   300  
   301  // Used for UnionType.
   302  type UnionArg struct {
   303  	ArgCommon
   304  	Option Arg
   305  	Index  int // Index of the selected option in the union type.
   306  	// Used for unions with conditional fields.
   307  	// We first create a dummy arg with transient=True and then
   308  	// patch them.
   309  	transient bool
   310  }
   311  
   312  func MakeUnionArg(t Type, dir Dir, opt Arg, index int) *UnionArg {
   313  	return &UnionArg{ArgCommon: ArgCommon{ref: t.ref(), dir: dir}, Option: opt, Index: index}
   314  }
   315  
   316  func (arg *UnionArg) Size() uint64 {
   317  	if !arg.Type().Varlen() {
   318  		return arg.Type().Size()
   319  	}
   320  	return arg.Option.Size()
   321  }
   322  
   323  // Used for ResourceType.
   324  // This is the only argument that can be used as syscall return value.
   325  // Either holds constant value or reference another ResultArg.
   326  type ResultArg struct {
   327  	ArgCommon
   328  	Res   *ResultArg          // reference to arg which we use
   329  	OpDiv uint64              // divide result (executed before OpAdd)
   330  	OpAdd uint64              // add to result
   331  	Val   uint64              // value used if Res is nil
   332  	uses  map[*ResultArg]bool // args that use this arg
   333  }
   334  
   335  func MakeResultArg(t Type, dir Dir, r *ResultArg, v uint64) *ResultArg {
   336  	arg := &ResultArg{ArgCommon: ArgCommon{ref: t.ref(), dir: dir}, Res: r, Val: v}
   337  	if r == nil {
   338  		return arg
   339  	}
   340  	if r.uses == nil {
   341  		r.uses = make(map[*ResultArg]bool)
   342  	}
   343  	r.uses[arg] = true
   344  	return arg
   345  }
   346  
   347  func MakeReturnArg(t Type) *ResultArg {
   348  	if t == nil {
   349  		return nil
   350  	}
   351  	return &ResultArg{ArgCommon: ArgCommon{ref: t.ref(), dir: DirOut}}
   352  }
   353  
   354  func (arg *ResultArg) Size() uint64 {
   355  	return arg.Type().Size()
   356  }
   357  
   358  // Returns inner arg for pointer args.
   359  func InnerArg(arg Arg) Arg {
   360  	if _, ok := arg.Type().(*PtrType); ok {
   361  		res := arg.(*PointerArg).Res
   362  		if res == nil {
   363  			return nil
   364  		}
   365  		return InnerArg(res)
   366  	}
   367  	return arg // Not a pointer.
   368  }
   369  
   370  func isDefault(arg Arg) bool {
   371  	return arg.Type().isDefaultArg(arg)
   372  }
   373  
   374  func (p *Prog) insertBefore(c *Call, calls []*Call) {
   375  	idx := 0
   376  	for ; idx < len(p.Calls); idx++ {
   377  		if p.Calls[idx] == c {
   378  			break
   379  		}
   380  	}
   381  	var newCalls []*Call
   382  	newCalls = append(newCalls, p.Calls[:idx]...)
   383  	newCalls = append(newCalls, calls...)
   384  	if idx < len(p.Calls) {
   385  		newCalls = append(newCalls, p.Calls[idx])
   386  		newCalls = append(newCalls, p.Calls[idx+1:]...)
   387  	}
   388  	p.Calls = newCalls
   389  }
   390  
   391  // replaceArg replaces arg with arg1 in a program.
   392  func replaceArg(arg, arg1 Arg) {
   393  	if arg == arg1 {
   394  		panic("replacing an argument with itself")
   395  	}
   396  	switch a := arg.(type) {
   397  	case *ConstArg:
   398  		*a = *arg1.(*ConstArg)
   399  	case *ResultArg:
   400  		replaceResultArg(a, arg1.(*ResultArg))
   401  	case *PointerArg:
   402  		*a = *arg1.(*PointerArg)
   403  	case *UnionArg:
   404  		if a.Option != nil {
   405  			removeArg(a.Option)
   406  		}
   407  		*a = *arg1.(*UnionArg)
   408  	case *DataArg:
   409  		*a = *arg1.(*DataArg)
   410  	case *GroupArg:
   411  		_, isStruct := arg.Type().(*StructType)
   412  		a1 := arg1.(*GroupArg)
   413  		if isStruct && len(a.Inner) != len(a1.Inner) {
   414  			panic(fmt.Sprintf("replaceArg: group fields don't match: %v/%v",
   415  				len(a.Inner), len(a1.Inner)))
   416  		}
   417  		a.ArgCommon = a1.ArgCommon
   418  		// Replace min(|a|, |a1|) arguments.
   419  		for i := 0; i < len(a.Inner) && i < len(a1.Inner); i++ {
   420  			replaceArg(a.Inner[i], a1.Inner[i])
   421  		}
   422  		// Remove extra arguments of a.
   423  		for len(a.Inner) > len(a1.Inner) {
   424  			i := len(a.Inner) - 1
   425  			removeArg(a.Inner[i])
   426  			a.Inner[i] = nil
   427  			a.Inner = a.Inner[:i]
   428  		}
   429  		// Add extra arguments to a.
   430  		for i := len(a.Inner); i < len(a1.Inner); i++ {
   431  			a.Inner = append(a.Inner, a1.Inner[i])
   432  		}
   433  		if debug && len(a.Inner) != len(a1.Inner) {
   434  			panic("replaceArg implementation bug")
   435  		}
   436  	default:
   437  		panic(fmt.Sprintf("replaceArg: bad arg kind %#v", arg))
   438  	}
   439  }
   440  
   441  func replaceResultArg(arg, arg1 *ResultArg) {
   442  	// Remove link from `a.Res` to `arg`.
   443  	if arg.Res != nil {
   444  		delete(arg.Res.uses, arg)
   445  	}
   446  	// Copy all fields from `arg1` to `arg` except for the list of args that use `arg`.
   447  	uses := arg.uses
   448  	*arg = *arg1
   449  	arg.uses = uses
   450  	// Make the link in `arg.Res` (which is now `Res` of `arg1`) to point to `arg` instead of `arg1`.
   451  	if arg.Res != nil {
   452  		resUses := arg.Res.uses
   453  		delete(resUses, arg1)
   454  		resUses[arg] = true
   455  	}
   456  }
   457  
   458  // removeArg removes all references to/from arg0 from a program.
   459  func removeArg(arg0 Arg) {
   460  	ForeachSubArg(arg0, func(arg Arg, ctx *ArgCtx) {
   461  		a, ok := arg.(*ResultArg)
   462  		if !ok {
   463  			return
   464  		}
   465  		if a.Res != nil {
   466  			uses := a.Res.uses
   467  			if !uses[a] {
   468  				panic("broken tree")
   469  			}
   470  			delete(uses, a)
   471  		}
   472  		for arg1 := range a.uses {
   473  			arg2 := arg1.Type().DefaultArg(arg1.Dir()).(*ResultArg)
   474  			replaceResultArg(arg1, arg2)
   475  		}
   476  	})
   477  }
   478  
   479  // RemoveCall removes call idx from p.
   480  func (p *Prog) RemoveCall(idx int) {
   481  	c := p.Calls[idx]
   482  	for _, arg := range c.Args {
   483  		removeArg(arg)
   484  	}
   485  	if c.Ret != nil {
   486  		removeArg(c.Ret)
   487  	}
   488  	copy(p.Calls[idx:], p.Calls[idx+1:])
   489  	p.Calls = p.Calls[:len(p.Calls)-1]
   490  }
   491  
   492  func (p *Prog) sanitizeFix() {
   493  	if err := p.sanitize(true); err != nil {
   494  		panic(err)
   495  	}
   496  }
   497  
   498  // FormatArg returns a string slice representation of an argument with one
   499  // entry per line in the output. The formatting roughly corresponds to syzlang
   500  // descriptions and is intended to be human readable.
   501  func FormatArg(arg Arg, name string) []string {
   502  	const indent string = "  " // Two spaces.
   503  	makeIndent := func(level int) string {
   504  		return strings.Repeat(indent, level)
   505  	}
   506  
   507  	// Depth-first search starting at initial argument, incrementing the indent
   508  	// level as we go deeper.
   509  	var visit func(Arg, string, int) []string
   510  	visit = func(arg Arg, name string, depth int) []string {
   511  		var lines []string
   512  
   513  		var lineBuilder strings.Builder
   514  		lineBuilder.WriteString(makeIndent(depth))
   515  
   516  		if name != "" {
   517  			fmt.Fprintf(&lineBuilder, "%s: ", name)
   518  		}
   519  
   520  		switch a := arg.(type) {
   521  		case *GroupArg:
   522  			fmt.Fprintf(&lineBuilder, "%s {", a.Type().String())
   523  			lines = append(lines, lineBuilder.String())
   524  
   525  			s, isStruct := a.ArgCommon.Type().(*StructType)
   526  			for i, inner := range a.Inner {
   527  				// For GroupArgs, only those of type StructType have named
   528  				// children.
   529  				childName := ""
   530  				if isStruct {
   531  					childName = s.Fields[i].Name
   532  				}
   533  				lines = append(lines, visit(inner, childName, depth+1)...)
   534  			}
   535  			lines = append(lines, makeIndent(depth)+"}")
   536  		case *ConstArg:
   537  			fmt.Fprintf(&lineBuilder, "%s = 0x%x (%d bytes)", a.Type().Name(), a.Val, a.Size())
   538  			lines = append(lines, lineBuilder.String())
   539  		case *DataArg:
   540  			tpe, ok := a.Type().(*BufferType)
   541  			if !ok {
   542  				panic("data args should be a buffer type")
   543  			}
   544  
   545  			fmt.Fprintf(&lineBuilder, "%s: ", a.Type().String())
   546  
   547  			// Result buffer - nothing to display.
   548  			if a.Dir() == DirOut {
   549  				fmt.Fprint(&lineBuilder, "(DirOut)")
   550  			} else {
   551  				// Compressed buffers (e.g., fs images) tend to be very large
   552  				// and it doesn't make much sense to output their contents.
   553  				if tpe.Kind == BufferCompressed {
   554  					fmt.Fprintf(&lineBuilder, "(compressed buffer with length 0x%x)", len(a.Data()))
   555  				} else {
   556  					fmt.Fprintf(&lineBuilder, "{% x} (length 0x%x)", a.Data(), len(a.Data()))
   557  				}
   558  			}
   559  			lines = append(lines, lineBuilder.String())
   560  		case *PointerArg:
   561  			if a.Res != nil {
   562  				fmt.Fprintf(&lineBuilder, "%s {", a.Type().String())
   563  				lines = append(lines, lineBuilder.String())
   564  				lines = append(lines, visit(a.Res, "", depth+1)...)
   565  				lines = append(lines, makeIndent(depth)+"}")
   566  			} else {
   567  				if a.VmaSize == 0 {
   568  					lineBuilder.WriteString("nil")
   569  				} else {
   570  					fmt.Fprintf(&lineBuilder, "VMA[0x%x]", a.VmaSize)
   571  				}
   572  				lines = append(lines, lineBuilder.String())
   573  			}
   574  		case *UnionArg:
   575  			union, ok := a.ArgCommon.Type().(*UnionType)
   576  			if !ok {
   577  				panic("a UnionArg should have an ArgCommon of type UnionType")
   578  			}
   579  			fmt.Fprintf(&lineBuilder, "union %s {", a.Type().Name())
   580  			lines = append(lines, lineBuilder.String())
   581  			if a.Option != nil {
   582  				lines = append(lines, visit(a.Option, union.Fields[a.Index].Name, depth+1)...)
   583  			}
   584  			lines = append(lines, makeIndent(depth)+"}")
   585  		case *ResultArg:
   586  			fmt.Fprintf(&lineBuilder, "%s (resource)", a.ArgCommon.Type().String())
   587  			lines = append(lines, lineBuilder.String())
   588  		default:
   589  			// We shouldn't hit this because the switch statements cover every
   590  			// prog.Arg implementation.
   591  			panic("Unsupported argument type.")
   592  		}
   593  		return lines
   594  	}
   595  
   596  	return visit(arg, name, 0)
   597  }
   598  
   599  func (p *Prog) sanitize(fix bool) error {
   600  	for _, c := range p.Calls {
   601  		if err := p.Target.sanitize(c, fix); err != nil {
   602  			return err
   603  		}
   604  	}
   605  	return nil
   606  }
   607  
   608  // TODO: This method might be more generic - it can be applied to any struct.
   609  func (props *CallProps) ForeachProp(f func(fieldName, key string, value reflect.Value)) {
   610  	valueObj := reflect.ValueOf(props).Elem()
   611  	typeObj := valueObj.Type()
   612  	for i := 0; i < valueObj.NumField(); i++ {
   613  		fieldValue := valueObj.Field(i)
   614  		fieldType := typeObj.Field(i)
   615  		f(fieldType.Name, fieldType.Tag.Get("key"), fieldValue)
   616  	}
   617  }