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") } }
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") }