示例#1
0
func (r *randGen) generateArg(s *state, typ sys.Type, dir ArgDir) (arg *Arg, calls []*Call) {
	if dir == DirOut {
		// No need to generate something interesting for output scalar arguments.
		// But we still need to generate the argument itself so that it can be referenced
		// in subsequent calls. For the same reason we do generate pointer/array/struct
		// output arguments (their elements can be referenced in subsequent calls).
		switch typ.(type) {
		case sys.IntType, sys.FlagsType, sys.ConstType, sys.StrConstType, sys.FileoffType, sys.ResourceType:
			return constArg(0), nil
		}
	}

	if typ.Optional() && r.oneOf(5) {
		if _, ok := typ.(sys.BufferType); ok {
			panic("impossible") // parent PtrType must be Optional instead
		}
		return constArg(typ.Default()), nil
	}

	switch a := typ.(type) {
	case sys.ResourceType:
		r.choose(
			1, func() {
				special := a.SpecialValues()
				arg = constArg(special[r.Intn(len(special))])
			},
			90, func() {
				// Get an existing resource.
				var allres []*Arg
				for name1, res1 := range s.resources {
					if sys.IsCompatibleResource(a.Desc.Name, name1) ||
						r.oneOf(20) && sys.IsCompatibleResource(a.Desc.Kind[0], name1) {
						allres = append(allres, res1...)
					}
				}
				if len(allres) != 0 {
					arg = resultArg(allres[r.Intn(len(allres))])
				} else {
					arg, calls = r.createResource(s, a)
				}
			},
			5, func() {
				// Create a new resource.
				arg, calls = r.createResource(s, a)
			},
		)
		return arg, calls
	case sys.FileoffType:
		// TODO: can do better
		var arg *Arg
		r.choose(
			90, func() { arg = constArg(0) },
			10, func() { arg = constArg(r.rand(100)) },
			1, func() { arg = constArg(r.randInt()) },
		)
		return arg, nil
	case sys.BufferType:
		switch a.Kind {
		case sys.BufferBlobRand, sys.BufferBlobRange:
			sz := r.randBufLen()
			if a.Kind == sys.BufferBlobRange {
				sz = r.randRange(int(a.RangeBegin), int(a.RangeEnd))
			}
			data := make([]byte, sz)
			if dir != DirOut {
				for i := range data {
					data[i] = byte(r.Intn(256))
				}
			}
			return dataArg(data), nil
		case sys.BufferString:
			data := r.randString(s)
			return dataArg(data), nil
		case sys.BufferFilesystem:
			data := r.filesystem(s)
			return dataArg(data), nil
		case sys.BufferSockaddr:
			data := r.sockaddr(s)
			if dir == DirOut {
				for i := range data {
					data[i] = 0
				}
			}
			return dataArg(data), nil
		case sys.BufferAlgType:
			data := r.algType(s)
			if dir == DirOut {
				for i := range data {
					data[i] = 0
				}
			}
			return dataArg(data), nil
		case sys.BufferAlgName:
			data := r.algName(s)
			if dir == DirOut {
				for i := range data {
					data[i] = 0
				}
			}
			return dataArg(data), nil
		default:
			panic("unknown buffer kind")
		}
	case sys.VmaType:
		npages := r.randPageCount()
		arg := r.randPageAddr(s, npages, nil, true)
		return arg, nil
	case sys.FlagsType:
		return constArg(r.flags(a.Vals)), nil
	case sys.ConstType:
		return constArg(a.Val), nil
	case sys.StrConstType:
		return dataArg([]byte(a.Val)), nil
	case sys.IntType:
		v := r.randInt()
		switch a.Kind {
		case sys.IntSignalno:
			v %= 130
		case sys.IntInaddr:
			v = uintptr(r.inaddr(s))
		case sys.IntInport:
			v = uintptr(r.inport(s))
		case sys.IntRange:
			v = r.randRangeInt(a.RangeBegin, a.RangeEnd)
		}
		return constArg(v), nil
	case sys.FilenameType:
		filename := r.filename(s)
		return dataArg([]byte(filename)), nil
	case sys.ArrayType:
		count := uintptr(0)
		switch a.Kind {
		case sys.ArrayRandLen:
			count = r.rand(6)
		case sys.ArrayRangeLen:
			count = r.randRange(int(a.RangeBegin), int(a.RangeEnd))
		}
		var inner []*Arg
		var calls []*Call
		for i := uintptr(0); i < count; i++ {
			arg1, calls1 := r.generateArg(s, a.Type, dir)
			inner = append(inner, arg1)
			calls = append(calls, calls1...)
		}
		return groupArg(inner), calls
	case *sys.StructType:
		if ctor := isSpecialStruct(a); ctor != nil && dir != DirOut {
			arg, calls = ctor(r, s)
			return
		}
		args, calls := r.generateArgs(s, a.Fields, dir)
		group := groupArg(args)
		return group, calls
	case *sys.UnionType:
		optType := a.Options[r.Intn(len(a.Options))]
		opt, calls := r.generateArg(s, optType, dir)
		return unionArg(opt, optType), calls
	case sys.PtrType:
		inner, calls := r.generateArg(s, a.Type, ArgDir(a.Dir))
		if ArgDir(a.Dir) == DirOut && inner == nil {
			// No data, but we should have got size.
			arg, calls1 := r.addr(s, inner.Size(a.Type), nil)
			calls = append(calls, calls1...)
			return arg, calls
		}
		if a.Type.Name() == "iocb" && len(s.resources["iocbptr"]) != 0 {
			// It is weird, but these are actually identified by kernel by address.
			// So try to reuse a previously used address.
			addrs := s.resources["iocbptr"]
			addr := addrs[r.Intn(len(addrs))]
			arg = pointerArg(addr.AddrPage, addr.AddrOffset, addr.AddrPagesNum, inner)
			return arg, calls
		}
		arg, calls1 := r.addr(s, inner.Size(a.Type), inner)
		calls = append(calls, calls1...)
		return arg, calls
	case sys.LenType:
		// Return placeholder value of 0 while generating len args.
		return constArg(0), nil
	default:
		panic("unknown argument type")
	}
}
示例#2
0
func (r *randGen) createResource(s *state, res sys.ResourceType) (arg *Arg, calls []*Call) {
	if r.inCreateResource {
		special := res.SpecialValues()
		return constArg(special[r.Intn(len(special))]), nil
	}
	r.inCreateResource = true
	defer func() { r.inCreateResource = false }()

	kind := res.Desc.Name
	if r.oneOf(100) {
		// Spoof resource subkind.
		var all []string
		for kind1 := range sys.Resources {
			if sys.IsCompatibleResource(res.Desc.Kind[0], kind1) {
				all = append(all, kind1)
			}
		}
		kind = all[r.Intn(len(all))]
	}
	// Find calls that produce the necessary resources.
	metas0 := sys.ResourceConstructors(kind)
	// TODO: reduce priority of less specialized ctors.
	var metas []*sys.Call
	for _, meta := range metas0 {
		if s.ct == nil || s.ct.run[meta.ID] == nil {
			continue
		}
		metas = append(metas, meta)
	}
	if len(metas) == 0 {
		return constArg(res.Default()), nil
	}

	// Now we have a set of candidate calls that can create the necessary resource.
	for i := 0; i < 1e3; i++ {
		// Generate one of them.
		meta := metas[r.Intn(len(metas))]
		calls := r.generateParticularCall(s, meta)
		s1 := newState(s.ct)
		s1.analyze(calls[len(calls)-1])
		// Now see if we have what we want.
		var allres []*Arg
		for kind1, res1 := range s1.resources {
			if sys.IsCompatibleResource(kind, kind1) {
				allres = append(allres, res1...)
			}
		}
		if len(allres) != 0 {
			// Bingo!
			arg := resultArg(allres[r.Intn(len(allres))])
			return arg, calls
		}
		switch meta.Name {
		// Return resources in a variable-length array (length can be 0).
		case "getgroups", "ioctl$DRM_IOCTL_RES_CTX":
		default:
			panic(fmt.Sprintf("unexpected call failed to create a resource %v: %v", kind, meta.Name))
		}
		// Discard unsuccessful calls.
		for _, c := range calls {
			foreachArg(c, func(arg, _ *Arg, _ *[]*Arg) {
				if arg.Kind == ArgResult {
					delete(arg.Res.Uses, arg)
				}
			})
		}
	}
	// Generally we can loop several times, e.g. when we choose a call that returns
	// the resource in an array, but then generateArg generated that array of zero length.
	// But we must succeed eventually.
	panic("failed to create a resource")
}