github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/prog/analysis.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  // Conservative resource-related analysis of programs.
     5  // The analysis figures out what files descriptors are [potentially] opened
     6  // at a particular point in program, what pages are [potentially] mapped,
     7  // what files were already referenced in calls, etc.
     8  
     9  package prog
    10  
    11  import (
    12  	"bytes"
    13  	"fmt"
    14  	"io"
    15  
    16  	"github.com/google/syzkaller/pkg/image"
    17  )
    18  
    19  type state struct {
    20  	target    *Target
    21  	ct        *ChoiceTable
    22  	corpus    []*Prog
    23  	files     map[string]bool
    24  	resources map[string][]*ResultArg
    25  	strings   map[string]bool
    26  	ma        *memAlloc
    27  	va        *vmaAlloc
    28  }
    29  
    30  // analyze analyzes the program p up to but not including call c.
    31  func analyze(ct *ChoiceTable, corpus []*Prog, p *Prog, c *Call) *state {
    32  	s := newState(p.Target, ct, corpus)
    33  	resources := true
    34  	for _, c1 := range p.Calls {
    35  		if c1 == c {
    36  			resources = false
    37  		}
    38  		s.analyzeImpl(c1, resources)
    39  	}
    40  	return s
    41  }
    42  
    43  func newState(target *Target, ct *ChoiceTable, corpus []*Prog) *state {
    44  	s := &state{
    45  		target:    target,
    46  		ct:        ct,
    47  		corpus:    corpus,
    48  		files:     make(map[string]bool),
    49  		resources: make(map[string][]*ResultArg),
    50  		strings:   make(map[string]bool),
    51  		ma:        newMemAlloc(target.NumPages * target.PageSize),
    52  		va:        newVmaAlloc(target.NumPages),
    53  	}
    54  	return s
    55  }
    56  
    57  func (s *state) analyze(c *Call) {
    58  	s.analyzeImpl(c, true)
    59  }
    60  
    61  func (s *state) analyzeImpl(c *Call, resources bool) {
    62  	ForeachArg(c, func(arg Arg, _ *ArgCtx) {
    63  		switch a := arg.(type) {
    64  		case *PointerArg:
    65  			switch {
    66  			case a.IsSpecial():
    67  			case a.VmaSize != 0:
    68  				s.va.noteAlloc(a.Address/s.target.PageSize, a.VmaSize/s.target.PageSize)
    69  			case a.Res != nil:
    70  				s.ma.noteAlloc(a.Address, a.Res.Size())
    71  			}
    72  		}
    73  		switch typ := arg.Type().(type) {
    74  		case *ResourceType:
    75  			a := arg.(*ResultArg)
    76  			if resources && a.Dir() != DirIn {
    77  				s.resources[typ.Desc.Name] = append(s.resources[typ.Desc.Name], a)
    78  				// TODO: negative PIDs and add them as well (that's process groups).
    79  			}
    80  		case *BufferType:
    81  			a := arg.(*DataArg)
    82  			if a.Dir() != DirOut && len(a.Data()) != 0 &&
    83  				(typ.Kind == BufferString || typ.Kind == BufferFilename) {
    84  				val := string(a.Data())
    85  				// Remove trailing zero padding.
    86  				for len(val) >= 2 && val[len(val)-1] == 0 && val[len(val)-2] == 0 {
    87  					val = val[:len(val)-1]
    88  				}
    89  				switch typ.Kind {
    90  				case BufferString:
    91  					s.strings[val] = true
    92  				case BufferFilename:
    93  					if len(val) < 3 || escapingFilename(val) {
    94  						// This is not our file, probalby one of specialFiles.
    95  						return
    96  					}
    97  					if val[len(val)-1] == 0 {
    98  						val = val[:len(val)-1]
    99  					}
   100  					s.files[val] = true
   101  				}
   102  			}
   103  		}
   104  	})
   105  }
   106  
   107  type parentStack []Arg
   108  
   109  func allocStack() parentStack {
   110  	// Let's save some allocations during stack traversal.
   111  	return make([]Arg, 0, 4)
   112  }
   113  
   114  func pushStack(ps parentStack, a Arg) parentStack {
   115  	return append(ps, a)
   116  }
   117  
   118  func popStack(ps parentStack) (parentStack, Arg) {
   119  	if len(ps) > 0 {
   120  		return ps[:len(ps)-1], ps[len(ps)-1]
   121  	}
   122  	return ps, nil
   123  }
   124  
   125  type ArgCtx struct {
   126  	Parent      *[]Arg      // GroupArg.Inner (for structs) or Call.Args containing this arg.
   127  	Fields      []Field     // Fields of the parent struct/syscall.
   128  	Base        *PointerArg // Pointer to the base of the heap object containing this arg.
   129  	Offset      uint64      // Offset of this arg from the base.
   130  	Stop        bool        // If set by the callback, subargs of this arg are not visited.
   131  	parentStack parentStack // Struct and union arguments by which the argument can be reached.
   132  }
   133  
   134  func ForeachSubArg(arg Arg, f func(Arg, *ArgCtx)) {
   135  	foreachArgImpl(arg, &ArgCtx{}, f)
   136  }
   137  
   138  func foreachSubArgWithStack(arg Arg, f func(Arg, *ArgCtx)) {
   139  	foreachArgImpl(arg, &ArgCtx{parentStack: allocStack()}, f)
   140  }
   141  
   142  func ForeachArg(c *Call, f func(Arg, *ArgCtx)) {
   143  	ctx := &ArgCtx{}
   144  	if c.Ret != nil {
   145  		foreachArgImpl(c.Ret, ctx, f)
   146  	}
   147  	ctx.Parent = &c.Args
   148  	ctx.Fields = c.Meta.Args
   149  	for _, arg := range c.Args {
   150  		foreachArgImpl(arg, ctx, f)
   151  	}
   152  }
   153  
   154  func foreachArgImpl(arg Arg, ctx *ArgCtx, f func(Arg, *ArgCtx)) {
   155  	ctx0 := *ctx
   156  	defer func() { *ctx = ctx0 }()
   157  
   158  	if ctx.parentStack != nil {
   159  		switch arg.Type().(type) {
   160  		case *StructType, *UnionType:
   161  			ctx.parentStack = pushStack(ctx.parentStack, arg)
   162  		}
   163  	}
   164  	f(arg, ctx)
   165  	if ctx.Stop {
   166  		return
   167  	}
   168  	switch a := arg.(type) {
   169  	case *GroupArg:
   170  		overlayField := 0
   171  		if typ, ok := a.Type().(*StructType); ok {
   172  			ctx.Parent = &a.Inner
   173  			ctx.Fields = typ.Fields
   174  			overlayField = typ.OverlayField
   175  		}
   176  		var totalSize uint64
   177  		for i, arg1 := range a.Inner {
   178  			if i == overlayField {
   179  				ctx.Offset = ctx0.Offset
   180  			}
   181  			foreachArgImpl(arg1, ctx, f)
   182  			size := arg1.Size()
   183  			ctx.Offset += size
   184  			if totalSize < ctx.Offset {
   185  				totalSize = ctx.Offset - ctx0.Offset
   186  			}
   187  		}
   188  		if debug {
   189  			claimedSize := a.Size()
   190  			varlen := a.Type().Varlen()
   191  			if varlen && totalSize > claimedSize || !varlen && totalSize != claimedSize {
   192  				panic(fmt.Sprintf("bad group arg size %v, should be <= %v for %#v type %#v",
   193  					totalSize, claimedSize, a, a.Type().Name()))
   194  			}
   195  		}
   196  	case *PointerArg:
   197  		if a.Res != nil {
   198  			ctx.Base = a
   199  			ctx.Offset = 0
   200  			foreachArgImpl(a.Res, ctx, f)
   201  		}
   202  	case *UnionArg:
   203  		foreachArgImpl(a.Option, ctx, f)
   204  	}
   205  }
   206  
   207  type RequiredFeatures struct {
   208  	Bitmasks       bool
   209  	Csums          bool
   210  	FaultInjection bool
   211  	Async          bool
   212  }
   213  
   214  func (p *Prog) RequiredFeatures() RequiredFeatures {
   215  	features := RequiredFeatures{}
   216  	for _, c := range p.Calls {
   217  		ForeachArg(c, func(arg Arg, _ *ArgCtx) {
   218  			if a, ok := arg.(*ConstArg); ok {
   219  				if a.Type().BitfieldOffset() != 0 || a.Type().BitfieldLength() != 0 {
   220  					features.Bitmasks = true
   221  				}
   222  			}
   223  			if _, ok := arg.Type().(*CsumType); ok {
   224  				features.Csums = true
   225  			}
   226  		})
   227  		if c.Props.FailNth > 0 {
   228  			features.FaultInjection = true
   229  		}
   230  		if c.Props.Async {
   231  			features.Async = true
   232  		}
   233  	}
   234  	return features
   235  }
   236  
   237  type CallFlags int
   238  
   239  const (
   240  	CallExecuted CallFlags = 1 << iota // was started at all
   241  	CallFinished                       // finished executing (rather than blocked forever)
   242  	CallBlocked                        // finished but blocked during execution
   243  )
   244  
   245  type CallInfo struct {
   246  	Flags  CallFlags
   247  	Errno  int
   248  	Signal []uint32
   249  }
   250  
   251  const (
   252  	fallbackSignalErrno = iota
   253  	fallbackSignalErrnoBlocked
   254  	fallbackSignalCtor
   255  	fallbackSignalFlags
   256  	// This allows us to have 2M syscalls and leaves 8 bits for 256 errno values.
   257  	// Linux currently have 133 errno's. Larger errno values will be truncated,
   258  	// which is acceptable for fallback coverage.
   259  	fallbackCallMask = 0x1fffff
   260  )
   261  
   262  func (p *Prog) FallbackSignal(info []CallInfo) {
   263  	resources := make(map[*ResultArg]*Call)
   264  	for i, c := range p.Calls {
   265  		inf := &info[i]
   266  		if inf.Flags&CallExecuted == 0 {
   267  			continue
   268  		}
   269  		id := c.Meta.ID
   270  		typ := fallbackSignalErrno
   271  		if inf.Flags&CallFinished != 0 && inf.Flags&CallBlocked != 0 {
   272  			typ = fallbackSignalErrnoBlocked
   273  		}
   274  		inf.Signal = append(inf.Signal, encodeFallbackSignal(typ, id, inf.Errno))
   275  		if c.Meta.Attrs.BreaksReturns {
   276  			break
   277  		}
   278  		if inf.Errno != 0 {
   279  			continue
   280  		}
   281  		ForeachArg(c, func(arg Arg, _ *ArgCtx) {
   282  			if a, ok := arg.(*ResultArg); ok {
   283  				resources[a] = c
   284  			}
   285  		})
   286  		// Specifically look only at top-level arguments,
   287  		// deeper arguments can produce too much false signal.
   288  		flags := 0
   289  		for _, arg := range c.Args {
   290  			flags = extractArgSignal(arg, id, flags, inf, resources)
   291  		}
   292  		if flags != 0 {
   293  			inf.Signal = append(inf.Signal,
   294  				encodeFallbackSignal(fallbackSignalFlags, id, flags))
   295  		}
   296  	}
   297  }
   298  
   299  func extractArgSignal(arg Arg, callID, flags int, inf *CallInfo, resources map[*ResultArg]*Call) int {
   300  	switch a := arg.(type) {
   301  	case *ResultArg:
   302  		flags <<= 1
   303  		if a.Res != nil {
   304  			ctor := resources[a.Res]
   305  			if ctor != nil {
   306  				inf.Signal = append(inf.Signal,
   307  					encodeFallbackSignal(fallbackSignalCtor, callID, ctor.Meta.ID))
   308  			}
   309  		} else {
   310  			if a.Val != a.Type().(*ResourceType).SpecialValues()[0] {
   311  				flags |= 1
   312  			}
   313  		}
   314  	case *ConstArg:
   315  		const width = 3
   316  		flags <<= width
   317  		switch typ := a.Type().(type) {
   318  		case *FlagsType:
   319  			if typ.BitMask {
   320  				for i, v := range typ.Vals {
   321  					if a.Val&v != 0 {
   322  						flags ^= 1 << (uint(i) % width)
   323  					}
   324  				}
   325  			} else {
   326  				for i, v := range typ.Vals {
   327  					if a.Val == v {
   328  						flags |= i % (1 << width)
   329  						break
   330  					}
   331  				}
   332  			}
   333  		case *LenType:
   334  			flags <<= 1
   335  			if a.Val == 0 {
   336  				flags |= 1
   337  			}
   338  		}
   339  	case *PointerArg:
   340  		flags <<= 1
   341  		if a.IsSpecial() {
   342  			flags |= 1
   343  		}
   344  	}
   345  	return flags
   346  }
   347  
   348  func DecodeFallbackSignal(s uint32) (callID, errno int) {
   349  	typ, id, aux := decodeFallbackSignal(s)
   350  	switch typ {
   351  	case fallbackSignalErrno, fallbackSignalErrnoBlocked:
   352  		return id, aux
   353  	case fallbackSignalCtor, fallbackSignalFlags:
   354  		return id, 0
   355  	default:
   356  		panic(fmt.Sprintf("bad fallback signal type %v", typ))
   357  	}
   358  }
   359  
   360  func encodeFallbackSignal(typ, id, aux int) uint32 {
   361  	checkMaxCallID(id)
   362  	if typ & ^7 != 0 {
   363  		panic(fmt.Sprintf("bad fallback signal type %v", typ))
   364  	}
   365  	return uint32(typ) | uint32(id&fallbackCallMask)<<3 | uint32(aux)<<24
   366  }
   367  
   368  func decodeFallbackSignal(s uint32) (typ, id, aux int) {
   369  	return int(s & 7), int((s >> 3) & fallbackCallMask), int(s >> 24)
   370  }
   371  
   372  func checkMaxCallID(id int) {
   373  	if id & ^fallbackCallMask != 0 {
   374  		panic(fmt.Sprintf("too many syscalls, have %v, max supported %v", id, fallbackCallMask+1))
   375  	}
   376  }
   377  
   378  type AssetType int
   379  
   380  const (
   381  	MountInRepro AssetType = iota
   382  )
   383  
   384  func (p *Prog) ForEachAsset(cb func(name string, typ AssetType, r io.Reader)) {
   385  	for id, c := range p.Calls {
   386  		ForeachArg(c, func(arg Arg, _ *ArgCtx) {
   387  			a, ok := arg.(*DataArg)
   388  			if !ok || a.Type().(*BufferType).Kind != BufferCompressed {
   389  				return
   390  			}
   391  			data, dtor := image.MustDecompress(a.Data())
   392  			defer dtor()
   393  			if len(data) == 0 {
   394  				return
   395  			}
   396  			cb(fmt.Sprintf("mount_%v", id), MountInRepro, bytes.NewReader(data))
   397  		})
   398  	}
   399  }
   400  
   401  func (p *Prog) ContainsAny() bool {
   402  	for _, c := range p.Calls {
   403  		if p.Target.CallContainsAny(c) {
   404  			return true
   405  		}
   406  	}
   407  	return false
   408  }