github.com/google/syzkaller@v0.0.0-20251211124644-a066d2bc4b02/prog/validation.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  	"os"
     9  	"strings"
    10  )
    11  
    12  var debug = false // enabled in tests and fuzzers
    13  
    14  func init() {
    15  	// Enable debug checking in all tests.
    16  	if strings.HasSuffix(os.Args[0], ".test") {
    17  		debug = true
    18  	}
    19  }
    20  
    21  func (p *Prog) debugValidate() {
    22  	if debug {
    23  		if err := p.validate(); err != nil {
    24  			panic(err)
    25  		}
    26  	}
    27  }
    28  
    29  func (p *Prog) validate() error {
    30  	return p.validateWithOpts(validationOptions{})
    31  }
    32  
    33  type validCtx struct {
    34  	target      *Target
    35  	isUnsafe    bool
    36  	opts        validationOptions
    37  	args        map[Arg]bool
    38  	uses        map[Arg]Arg
    39  	currentCall *Call
    40  }
    41  
    42  type validationOptions struct {
    43  	ignoreTransient bool
    44  }
    45  
    46  func (p *Prog) validateWithOpts(opts validationOptions) error {
    47  	ctx := &validCtx{
    48  		target:   p.Target,
    49  		isUnsafe: p.isUnsafe,
    50  		opts:     opts,
    51  		args:     make(map[Arg]bool),
    52  		uses:     make(map[Arg]Arg),
    53  	}
    54  	for i, c := range p.Calls {
    55  		if c.Meta == nil {
    56  			return fmt.Errorf("call does not have meta information")
    57  		}
    58  		ctx.currentCall = c
    59  		if err := ctx.validateCall(c); err != nil {
    60  			return fmt.Errorf("call #%d %v: %w", i, c.Meta.Name, err)
    61  		}
    62  	}
    63  	ctx.currentCall = nil
    64  	for u, orig := range ctx.uses {
    65  		if !ctx.args[u] {
    66  			return fmt.Errorf("use of %+v referes to an out-of-tree arg\narg: %#v", orig, u)
    67  		}
    68  	}
    69  	return nil
    70  }
    71  
    72  func (ctx *validCtx) validateCall(c *Call) error {
    73  	if !ctx.isUnsafe && c.Meta.Attrs.Disabled {
    74  		return fmt.Errorf("use of a disabled call")
    75  	}
    76  	if c.Props.Rerun > 0 && c.Props.FailNth > 0 {
    77  		return fmt.Errorf("rerun > 0 && fail_nth > 0")
    78  	}
    79  	if len(c.Args) != len(c.Meta.Args) {
    80  		return fmt.Errorf("wrong number of arguments, want %v, got %v",
    81  			len(c.Meta.Args), len(c.Args))
    82  	}
    83  	for i, arg := range c.Args {
    84  		if err := ctx.validateArg(arg, c.Meta.Args[i].Type, DirIn); err != nil {
    85  			return err
    86  		}
    87  	}
    88  	if err := c.checkConditions(ctx.target, ctx.opts.ignoreTransient); err != nil {
    89  		return err
    90  	}
    91  	return ctx.validateRet(c)
    92  }
    93  
    94  func (ctx *validCtx) validateRet(c *Call) error {
    95  	if c.Meta.Ret == nil {
    96  		if c.Ret != nil {
    97  			return fmt.Errorf("return value without type")
    98  		}
    99  		return nil
   100  	}
   101  	if c.Ret == nil {
   102  		return fmt.Errorf("return value is absent")
   103  	}
   104  	if c.Ret.Res != nil || c.Ret.Val != 0 || c.Ret.OpDiv != 0 || c.Ret.OpAdd != 0 {
   105  		return fmt.Errorf("return value %v is not empty", c.Ret)
   106  	}
   107  	return ctx.validateArg(c.Ret, c.Meta.Ret, DirOut)
   108  }
   109  
   110  func (ctx *validCtx) validateArg(arg Arg, typ Type, dir Dir) error {
   111  	if arg == nil {
   112  		return fmt.Errorf("nil arg")
   113  	}
   114  	if ctx.args[arg] {
   115  		return fmt.Errorf("arg %#v is referenced several times in the tree", arg)
   116  	}
   117  	if arg.Type() == nil {
   118  		return fmt.Errorf("no arg type")
   119  	}
   120  	if _, ok := typ.(*PtrType); ok {
   121  		dir = DirIn // pointers are always in
   122  	}
   123  	// We used to demand that Arg has exactly the same dir as Type, however,
   124  	// it leads to problems when dealing with ANYRES* types.
   125  	// If the resource was DirIn before squashing, we should not demand that
   126  	// it be DirInOut - it would only lead to mutations that make little sense.
   127  	// Let's only deny truly conflicting directions, e.g. DirIn vs DirOut.
   128  	if arg.Dir() != dir && dir != DirInOut {
   129  		return fmt.Errorf("arg %#v type %v has wrong dir %v, expect %v", arg, arg.Type(), arg.Dir(), dir)
   130  	}
   131  	if !ctx.target.isAnyPtr(arg.Type()) && arg.Type() != typ {
   132  		return fmt.Errorf("bad arg type %#v, expect %#v", arg.Type(), typ)
   133  	}
   134  	if ctx.currentCall.Meta.Attrs.NoSquash && ctx.target.isAnyPtr(arg.Type()) {
   135  		return fmt.Errorf("ANY argument for no_squash call %v", ctx.currentCall.Meta.Name)
   136  	}
   137  	ctx.args[arg] = true
   138  	return arg.validate(ctx, dir)
   139  }
   140  
   141  func (arg *ConstArg) validate(ctx *validCtx, dir Dir) error {
   142  	switch typ := arg.Type().(type) {
   143  	case *IntType:
   144  		if arg.Dir() == DirOut && !isDefault(arg) {
   145  			return fmt.Errorf("out int arg '%v' has bad const value %v", typ.Name(), arg.Val)
   146  		}
   147  	case *ProcType:
   148  		if arg.Val >= typ.ValuesPerProc && !isDefault(arg) {
   149  			return fmt.Errorf("per proc arg '%v' has bad value %v", typ.Name(), arg.Val)
   150  		}
   151  	case *CsumType:
   152  		if arg.Val != 0 {
   153  			return fmt.Errorf("csum arg '%v' has nonzero value %v", typ.Name(), arg.Val)
   154  		}
   155  	case *ConstType, *FlagsType, *LenType:
   156  	default:
   157  		return fmt.Errorf("const arg %v has bad type %v", arg, typ.Name())
   158  	}
   159  	if arg.Dir() == DirOut {
   160  		// We generate output len arguments, which makes sense since it can be
   161  		// a length of a variable-length array which is not known otherwise.
   162  		typ := arg.Type()
   163  		if _, isLen := typ.(*LenType); !isLen {
   164  			if !typ.isDefaultArg(arg) {
   165  				return fmt.Errorf("output arg %q has non default value %+v", typ.Name(), arg)
   166  			}
   167  		}
   168  	}
   169  	return nil
   170  }
   171  
   172  func (arg *ResultArg) validate(ctx *validCtx, dir Dir) error {
   173  	typ, ok := arg.Type().(*ResourceType)
   174  	if !ok {
   175  		return fmt.Errorf("result arg %v has bad type %v", arg, arg.Type().Name())
   176  	}
   177  	for u := range arg.uses {
   178  		if u == nil {
   179  			return fmt.Errorf("nil reference in uses for arg %+v", arg)
   180  		}
   181  		if u.Res != arg {
   182  			return fmt.Errorf("result arg '%v' has broken uses link to (%+v)", arg, u)
   183  		}
   184  		ctx.uses[u] = arg
   185  	}
   186  	if arg.Dir() == DirOut && arg.Val != 0 && arg.Val != typ.Default() {
   187  		return fmt.Errorf("out resource arg '%v' has bad const value %v", typ.Name(), arg.Val)
   188  	}
   189  	if arg.Res != nil {
   190  		if !ctx.args[arg.Res] {
   191  			return fmt.Errorf("result arg %v references out-of-tree result: %#v -> %#v",
   192  				typ.Name(), arg, arg.Res)
   193  		}
   194  		if !arg.Res.uses[arg] {
   195  			return fmt.Errorf("result arg '%v' has broken link (%+v)", typ.Name(), arg.Res.uses)
   196  		}
   197  	}
   198  	if arg.Dir() == DirIn && len(arg.uses) > 0 {
   199  		return fmt.Errorf("result arg '%v' is DirIn, but is used %d times", typ.Name(), len(arg.uses))
   200  	}
   201  	if len(arg.uses) > 0 && arg.Size() > 8 {
   202  		return fmt.Errorf("result arg '%v' is to be copied out, yet it's bigger than int64 (%d > 8)", typ.Name(), arg.Size())
   203  	}
   204  	return nil
   205  }
   206  
   207  func (arg *DataArg) validate(ctx *validCtx, dir Dir) error {
   208  	typ, ok := arg.Type().(*BufferType)
   209  	if !ok {
   210  		return fmt.Errorf("data arg %v has bad type %v", arg, arg.Type().Name())
   211  	}
   212  	if arg.Dir() == DirOut && len(arg.data) != 0 {
   213  		return fmt.Errorf("output arg '%v' has data", typ.Name())
   214  	}
   215  	if !typ.Varlen() && typ.Size() != arg.Size() {
   216  		return fmt.Errorf("data arg %v has wrong size %v, want %v",
   217  			typ.Name(), arg.Size(), typ.Size())
   218  	}
   219  	switch typ.Kind {
   220  	case BufferString:
   221  		if typ.TypeSize != 0 && arg.Size() != typ.TypeSize {
   222  			return fmt.Errorf("string arg '%v' has size %v, which should be %v",
   223  				typ.Name(), arg.Size(), typ.TypeSize)
   224  		}
   225  	case BufferFilename:
   226  		if !ctx.isUnsafe && escapingFilename(string(arg.data)) {
   227  			return fmt.Errorf("escaping filename %q", arg.data)
   228  		}
   229  	}
   230  	return nil
   231  }
   232  
   233  func (arg *GroupArg) validate(ctx *validCtx, dir Dir) error {
   234  	switch typ := arg.Type().(type) {
   235  	case *StructType:
   236  		if len(arg.Inner) != len(typ.Fields) {
   237  			return fmt.Errorf("struct arg '%v' has wrong number of fields: want %v, got %v",
   238  				typ.Name(), len(typ.Fields), len(arg.Inner))
   239  		}
   240  		for i, field := range arg.Inner {
   241  			if err := ctx.validateArg(field, typ.Fields[i].Type, typ.Fields[i].Dir(dir)); err != nil {
   242  				return err
   243  			}
   244  		}
   245  	case *ArrayType:
   246  		if typ.Kind == ArrayRangeLen && typ.RangeBegin == typ.RangeEnd &&
   247  			uint64(len(arg.Inner)) != typ.RangeBegin {
   248  			return fmt.Errorf("array %v has wrong number of elements %v, want %v",
   249  				typ.Name(), len(arg.Inner), typ.RangeBegin)
   250  		}
   251  		for _, elem := range arg.Inner {
   252  			if err := ctx.validateArg(elem, typ.Elem, dir); err != nil {
   253  				return err
   254  			}
   255  		}
   256  	default:
   257  		return fmt.Errorf("group arg %v has bad type %v", arg, typ.Name())
   258  	}
   259  	return nil
   260  }
   261  
   262  func (arg *UnionArg) validate(ctx *validCtx, dir Dir) error {
   263  	typ, ok := arg.Type().(*UnionType)
   264  	if !ok {
   265  		return fmt.Errorf("union arg %v has bad type %v", arg, arg.Type().Name())
   266  	}
   267  	if arg.Index < 0 || arg.Index >= len(typ.Fields) {
   268  		return fmt.Errorf("union arg %v has bad index %v/%v", arg, arg.Index, len(typ.Fields))
   269  	}
   270  	if arg.transient && !ctx.opts.ignoreTransient {
   271  		// The union must have been patched via Call.setDefaultConditions.
   272  		return fmt.Errorf("union arg %v is transient (incomplete)", arg)
   273  	}
   274  	opt := typ.Fields[arg.Index]
   275  	return ctx.validateArg(arg.Option, opt.Type, opt.Dir(dir))
   276  }
   277  
   278  func (arg *PointerArg) validate(ctx *validCtx, dir Dir) error {
   279  	switch typ := arg.Type().(type) {
   280  	case *VmaType:
   281  		if arg.Res != nil {
   282  			return fmt.Errorf("vma arg '%v' has data", typ.Name())
   283  		}
   284  	case *PtrType:
   285  		if arg.Res != nil {
   286  			if err := ctx.validateArg(arg.Res, typ.Elem, typ.ElemDir); err != nil {
   287  				return err
   288  			}
   289  		}
   290  		if arg.VmaSize != 0 {
   291  			return fmt.Errorf("pointer arg '%v' has nonzero size", typ.Name())
   292  		}
   293  		if arg.Dir() == DirOut {
   294  			return fmt.Errorf("pointer arg '%v' has output direction", typ.Name())
   295  		}
   296  	default:
   297  		return fmt.Errorf("ptr arg %v has bad type %v", arg, typ.Name())
   298  	}
   299  	if arg.IsSpecial() {
   300  		if -arg.Address >= uint64(len(ctx.target.SpecialPointers)) {
   301  			return fmt.Errorf("special ptr arg %v has bad value 0x%x", arg.Type().Name(), arg.Address)
   302  		}
   303  	} else {
   304  		maxMem := ctx.target.NumPages * ctx.target.PageSize
   305  		addr, size := arg.Address, arg.VmaSize
   306  		if size == 0 && arg.Res != nil {
   307  			size = arg.Res.Size()
   308  		}
   309  		if ctx.isUnsafe {
   310  			// Allow mapping 2 surrounding pages for DataMmapProg.
   311  			addr += ctx.target.PageSize
   312  			maxMem += 2 * ctx.target.PageSize
   313  		}
   314  		if addr >= maxMem || addr+size > maxMem {
   315  			return fmt.Errorf("ptr %v has bad address %v/%v/%v",
   316  				arg.Type().Name(), arg.Address, arg.VmaSize, size)
   317  		}
   318  	}
   319  	return nil
   320  }