func (a *Arg) Size(typ sys.Type) uintptr { switch typ1 := typ.(type) { case sys.IntType, sys.LenType, sys.FlagsType, sys.ConstType, sys.StrConstType, sys.FileoffType, sys.ResourceType, sys.VmaType, sys.PtrType: return typ.Size() case sys.FilenameType: return uintptr(len(a.Data)) case sys.BufferType: return uintptr(len(a.Data)) case sys.StructType: var size uintptr for i, f := range typ1.Fields { size += a.Inner[i].Size(f) } return size case sys.UnionType: return a.Option.Size(a.OptionType) case sys.ArrayType: var size uintptr for _, in := range a.Inner { size += in.Size(typ1.Type) } return size default: panic("unknown arg type") } }
// Returns inner arg for PtrType args func (a *Arg) InnerArg(typ sys.Type) *Arg { switch typ1 := typ.(type) { case sys.PtrType: if a.Res == nil { if typ.Optional() { return nil } else { panic(fmt.Sprintf("non-optional pointer is nil\narg: %+v\ntype: %+v", a, typ1)) } } else { return a.Res.InnerArg(typ1.Type) } default: return a } }
func isSpecialStruct(typ sys.Type) func(r *randGen, s *state) (*Arg, []*Call) { a, ok := typ.(*sys.StructType) if !ok { panic("must be a struct") } switch typ.Name() { case "timespec": return func(r *randGen, s *state) (*Arg, []*Call) { return r.timespec(s, a, false) } case "timeval": return func(r *randGen, s *state) (*Arg, []*Call) { return r.timespec(s, a, true) } } return nil }
func isSpecialStruct(typ sys.Type) func(r *randGen, s *state) (*Arg, []*Call) { if _, ok := typ.(*sys.StructType); !ok { panic("must be a struct") } switch typ.Name() { case "timespec": return func(r *randGen, s *state) (*Arg, []*Call) { return r.timespec(s, false) } case "timeval": return func(r *randGen, s *state) (*Arg, []*Call) { return r.timespec(s, true) } case "in6_addr": return func(r *randGen, s *state) (*Arg, []*Call) { return r.in6addr(s) } case "in_addr_any": return func(r *randGen, s *state) (*Arg, []*Call) { return r.inaddrany(s) } } return nil }
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) generateArg(s *state, typ sys.Type, dir ArgDir, sizes map[string]*Arg) (arg, size *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, nil } } if typ.Optional() && r.oneOf(10) { if _, ok := typ.(sys.BufferType); ok { panic("impossible") // parent PtrType must be Optional instead } return constArg(typ.Default()), constArg(0), 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. if ress := s.resources[a.Kind]; ress != nil { allres := ress[a.Subkind] allres = append(allres, ress[sys.ResAny]...) if a.Subkind == sys.ResAny || r.oneOf(10) { for _, v := range ress { allres = append(allres, v...) } } if len(allres) != 0 { // TODO: negative PIDs mean process group, // we should be able to negate an existing PID. arg = resultArg(allres[r.Intn(len(allres))]) } } if arg == nil { arg, calls = r.createResource(s, a) } }, 5, func() { // Create a new resource. arg, calls = r.createResource(s, a) }, ) return arg, nil, 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, nil case sys.BufferType: switch a.Kind { case sys.BufferBlob: sz := r.randBufLen() if dir == DirOut { return nil, constArg(sz), nil } data := make([]byte, sz) for i := range data { data[i] = byte(r.Intn(256)) } return dataArg(data), constArg(sz), nil case sys.BufferString: data := r.randString(s) return dataArg(data), constArg(uintptr(len(data))), nil case sys.BufferFilesystem: data := r.filesystem(s) return dataArg(data), constArg(uintptr(len(data))), nil case sys.BufferSockaddr: data := r.sockaddr(s) if dir == DirOut { return nil, constArg(uintptr(len(data))), nil } return dataArg(data), constArg(uintptr(len(data))), nil case sys.BufferAlgType: data := r.algType(s) if dir == DirOut { for i := range data { data[i] = 0 } } return dataArg(data), constArg(uintptr(len(data))), nil case sys.BufferAlgName: data := r.algName(s) if dir == DirOut { for i := range data { data[i] = 0 } } return dataArg(data), constArg(uintptr(len(data))), nil default: panic("unknown buffer kind") } case sys.VmaType: npages := r.randPageCount() arg := r.randPageAddr(s, npages, nil) return arg, pageSizeArg(npages, 0), nil case sys.FlagsType: return constArg(r.flags(a.Vals)), nil, nil case sys.ConstType: return constArg(a.Val), nil, nil case sys.StrConstType: return dataArg([]byte(a.Val)), constArg(uintptr(len(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)) } return constArg(v), nil, nil case sys.FilenameType: filename := r.filename(s) return dataArg([]byte(filename)), nil, nil case sys.ArrayType: count := a.Len if count == 0 { count = r.rand(6) } var inner []*Arg var calls []*Call for i := uintptr(0); i < count; i++ { arg1, _, calls1 := r.generateArg(s, a.Type, dir, nil) inner = append(inner, arg1) calls = append(calls, calls1...) } return groupArg(inner), constArg(count), 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) return groupArg(args), nil, calls case sys.UnionType: optType := a.Options[r.Intn(len(a.Options))] opt, size, calls := r.generateArg(s, optType, dir, sizes) return unionArg(opt, optType), size, calls case sys.PtrType: inner, size, calls := r.generateArg(s, a.Type, ArgDir(a.Dir), sizes) if ArgDir(a.Dir) == DirOut && inner == nil { // No data, but we should have got size. arg, calls1 := r.addr(s, size.Val, nil) calls = append(calls, calls1...) return arg, size, calls } if size == nil { size = constArg(inner.Size(a.Type)) } if a.Type.Name() == "iocb" && r.bin() && len(s.resources[sys.ResIocbPtr][sys.ResAny]) != 0 { // It is weird, but these are actually identified by kernel by address. // So try to reuse a previously used address. addrs := s.resources[sys.ResIocbPtr][sys.ResAny] addr := addrs[r.Intn(len(addrs))] arg = pointerArg(addr.AddrPage, addr.AddrOffset, inner) return arg, size, calls } arg, calls1 := r.addr(s, inner.Size(a.Type), inner) calls = append(calls, calls1...) return arg, size, calls case sys.LenType: if sizes == nil || sizes[a.Buf] == nil { fmt.Printf("name=%v buf=%v sizes=%+v\n", a.Name(), a.Buf, sizes) panic("me no generate len") } return sizes[a.Name()], nil, nil default: panic("unknown argument type") } }
func parseArg(typ sys.Type, p *parser, vars map[string]*Arg) (*Arg, error) { r := "" if p.Char() == '<' { p.Parse('<') r = p.Ident() p.Parse('=') p.Parse('>') } var arg *Arg switch p.Char() { case '0': val := p.Ident() v, err := strconv.ParseUint(val, 0, 64) if err != nil { return nil, fmt.Errorf("wrong arg value '%v': %v", val, err) } arg = constArg(uintptr(v)) case 'r': id := p.Ident() v, ok := vars[id] if !ok || v == nil { return nil, fmt.Errorf("result %v references unknown variable (vars=%+v)", id, vars) } arg = resultArg(v) if p.Char() == '/' { p.Parse('/') op := p.Ident() v, err := strconv.ParseUint(op, 0, 64) if err != nil { return nil, fmt.Errorf("wrong result div op: '%v'", op) } arg.OpDiv = uintptr(v) } if p.Char() == '+' { p.Parse('+') op := p.Ident() v, err := strconv.ParseUint(op, 0, 64) if err != nil { return nil, fmt.Errorf("wrong result add op: '%v'", op) } arg.OpAdd = uintptr(v) } case '&': var typ1 sys.Type switch t1 := typ.(type) { case sys.PtrType: typ1 = t1.Type case sys.VmaType: default: return nil, fmt.Errorf("& arg is not a pointer: %#v", typ) } p.Parse('&') page, off, size, err := parseAddr(p, true) if err != nil { return nil, err } p.Parse('=') inner, err := parseArg(typ1, p, vars) if err != nil { return nil, err } arg = pointerArg(page, off, size, inner) case '(': page, off, _, err := parseAddr(p, false) if err != nil { return nil, err } arg = pageSizeArg(page, off) case '"': p.Parse('"') val := "" if p.Char() != '"' { val = p.Ident() } p.Parse('"') data, err := hex.DecodeString(val) if err != nil { return nil, fmt.Errorf("data arg has bad value '%v'", val) } arg = dataArg(data) case '{': t1, ok := typ.(*sys.StructType) if !ok { return nil, fmt.Errorf("'{' arg is not a struct: %#v", typ) } p.Parse('{') var inner []*Arg for i := 0; p.Char() != '}'; i++ { if i >= len(t1.Fields) { return nil, fmt.Errorf("wrong struct arg count: %v, want %v", i+1, len(t1.Fields)) } fld := t1.Fields[i] if sys.IsPad(fld) { inner = append(inner, constArg(0)) } else { arg, err := parseArg(fld, p, vars) if err != nil { return nil, err } inner = append(inner, arg) if p.Char() != '}' { p.Parse(',') } } } p.Parse('}') if sys.IsPad(t1.Fields[len(t1.Fields)-1]) { inner = append(inner, constArg(0)) } arg = groupArg(inner) case '[': t1, ok := typ.(sys.ArrayType) if !ok { return nil, fmt.Errorf("'[' arg is not an array: %#v", typ) } p.Parse('[') var inner []*Arg for i := 0; p.Char() != ']'; i++ { arg, err := parseArg(t1.Type, p, vars) if err != nil { return nil, err } inner = append(inner, arg) if p.Char() != ']' { p.Parse(',') } } p.Parse(']') arg = groupArg(inner) case '@': t1, ok := typ.(*sys.UnionType) if !ok { return nil, fmt.Errorf("'@' arg is not a union: %#v", typ) } p.Parse('@') name := p.Ident() p.Parse('=') var optType sys.Type for _, t2 := range t1.Options { if name == t2.Name() { optType = t2 break } } if optType == nil { return nil, fmt.Errorf("union arg %v has unknown option: %v", typ.Name(), name) } opt, err := parseArg(optType, p, vars) if err != nil { return nil, err } arg = unionArg(opt, optType) case 'n': p.Parse('n') p.Parse('i') p.Parse('l') if r != "" { return nil, fmt.Errorf("named nil argument") } default: return nil, fmt.Errorf("failed to parse argument at %v (line #%v/%v: %v)", int(p.Char()), p.l, p.i, p.s) } if r != "" { vars[r] = arg } return arg, nil }
func returnArg(t sys.Type) *Arg { if t != nil { return &Arg{Type: t, Kind: ArgReturn, Val: t.Default()} } return &Arg{Type: t, Kind: ArgReturn} }
func (r *randGen) generateArg(s *state, typ sys.Type, dir ArgDir, sizes map[string]*Arg) (arg, size *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.FileoffType, sys.ResourceType: return constArg(0), nil, nil } } if typ.Optional() && r.oneOf(10) { if _, ok := typ.(sys.BufferType); ok { panic("impossible") // parent PtrType must be Optional instead } return constArg(typ.Default()), constArg(0), 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. if ress := s.resources[a.Kind]; ress != nil { allres := ress[a.Subkind] allres = append(allres, ress[sys.ResAny]...) if a.Subkind == sys.ResAny || r.oneOf(10) { for _, v := range ress { allres = append(allres, v...) } } if len(allres) != 0 { // TODO: negative PIDs mean process group, // we should be able to negate an existing PID. arg = resultArg(allres[r.Intn(len(allres))]) } } if arg == nil { arg, calls = r.createResource(s, a) } }, 10, func() { // Create a new resource. arg, calls = r.createResource(s, a) }, ) return arg, nil, 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, nil case sys.BufferType: switch a.Kind { case sys.BufferBlob: sz := r.randBufLen() if dir == DirOut { return nil, constArg(sz), nil } data := make([]byte, sz) for i := range data { data[i] = byte(r.Intn(256)) } return dataArg(data), constArg(sz), nil case sys.BufferString: data := r.randString(s) return dataArg(data), constArg(uintptr(len(data))), nil case sys.BufferSockaddr: data := r.sockaddr(s) if dir == DirOut { return nil, constArg(uintptr(len(data))), nil } return dataArg(data), constArg(uintptr(len(data))), nil default: panic("unknown buffer kind") } case sys.VmaType: npages := r.randPageCount() arg := r.randPageAddr(s, npages, nil) return arg, pageSizeArg(npages, 0), nil case sys.FlagsType: return constArg(r.flags(a.Vals)), nil, nil case sys.IntType: v := r.randInt() if a.Limit != 0 && !r.oneOf(100) { v %= a.Limit } return constArg(v), nil, nil case sys.FilenameType: filename := r.filename(s) return dataArg([]byte(filename)), nil, nil case sys.ArrayType: count := r.rand(6) var inner []*Arg var calls []*Call for i := uintptr(0); i < count; i++ { arg1, _, calls1 := r.generateArg(s, a.Type, dir, nil) inner = append(inner, arg1) calls = append(calls, calls1...) } return groupArg(inner), constArg(count), calls case sys.StructType: if dir != DirOut && (a.Name() == "timespec" || a.Name() == "timeval") { usec := a.Name() == "timeval" arg, calls = r.timespec(s, usec) return arg, nil, calls } args, calls := r.generateArgs(s, a.Fields, dir) return groupArg(args), nil, calls case sys.PtrType: inner, size, calls := r.generateArg(s, a.Type, ArgDir(a.Dir), sizes) if ArgDir(a.Dir) == DirOut && inner == nil { // No data, but we should have got size. arg, calls1 := r.addr(s, size.Val, nil) calls = append(calls, calls1...) return arg, size, calls } if size == nil { size = constArg(inner.Size(a.Type)) } arg, calls1 := r.addr(s, inner.Size(a.Type), inner) calls = append(calls, calls1...) return arg, size, calls case sys.LenType: if sizes == nil || sizes[a.Buf] == nil { fmt.Printf("name=%v buf=%v sizes=%+v\n", a.Name(), a.Buf, sizes) panic("me no generate len") } return sizes[a.Name()], nil, nil default: panic("unknown argument type") } }