diff options
| author | Dmitry Vyukov <dvyukov@google.com> | 2016-11-11 14:44:01 -0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2016-11-11 14:44:01 -0800 |
| commit | 89abacc228e60afe1df0b01d36dc7fe886ca7bcc (patch) | |
| tree | f0ca6508ab6f3b6ea78e6260c28dd09f37c9d48c /prog | |
| parent | 85f78e771dced807e5e09b8012ec38333e442bb7 (diff) | |
| parent | 3a65453870b12f5c42739c27d99df8fc58358f88 (diff) | |
Merge pull request #86 from google/sys_ptrs
A bunch of changes to sys/prog package
Diffstat (limited to 'prog')
| -rw-r--r-- | prog/analysis.go | 202 | ||||
| -rw-r--r-- | prog/clone.go | 1 | ||||
| -rw-r--r-- | prog/encoding.go | 38 | ||||
| -rw-r--r-- | prog/encodingexec.go | 16 | ||||
| -rw-r--r-- | prog/mutation.go | 86 | ||||
| -rw-r--r-- | prog/prio.go | 68 | ||||
| -rw-r--r-- | prog/prog.go | 120 | ||||
| -rw-r--r-- | prog/rand.go | 323 | ||||
| -rw-r--r-- | prog/size_test.go | 3 | ||||
| -rw-r--r-- | prog/validation.go | 35 |
10 files changed, 329 insertions, 563 deletions
diff --git a/prog/analysis.go b/prog/analysis.go index baf87fbb5..4bf6506f1 100644 --- a/prog/analysis.go +++ b/prog/analysis.go @@ -51,18 +51,19 @@ func newState(ct *ChoiceTable) *state { func (s *state) analyze(c *Call) { foreachArgArray(&c.Args, c.Ret, func(arg, base *Arg, _ *[]*Arg) { switch typ := arg.Type.(type) { - case sys.FilenameType: - if arg.Kind == ArgData && arg.Dir != DirOut { - s.files[string(arg.Data)] = true - } - case sys.ResourceType: - if arg.Dir != DirIn { + case *sys.ResourceType: + if arg.Type.Dir() != sys.DirIn { s.resources[typ.Desc.Name] = append(s.resources[typ.Desc.Name], arg) // TODO: negative PIDs and add them as well (that's process groups). } - case sys.BufferType: - if typ.Kind == sys.BufferString && arg.Kind == ArgData && len(arg.Data) != 0 { - s.strings[string(arg.Data)] = true + case *sys.BufferType: + if arg.Type.Dir() != sys.DirOut && arg.Kind == ArgData && len(arg.Data) != 0 { + switch typ.Kind { + case sys.BufferString: + s.strings[string(arg.Data)] = true + case sys.BufferFilename: + s.files[string(arg.Data)] = true + } } } }) @@ -149,186 +150,67 @@ func foreachArg(c *Call, f func(arg, base *Arg, parent *[]*Arg)) { foreachArgArray(&c.Args, nil, f) } -func assignTypeAndDir(c *Call) error { - var rec func(arg *Arg, typ sys.Type, dir ArgDir) error - rec = func(arg *Arg, typ sys.Type, dir ArgDir) error { - if arg.Call != nil && arg.Call != c { - panic(fmt.Sprintf("different call is already assigned: %p %p %v %v", arg.Call, c, arg.Call.Meta.Name, c.Meta.Name)) - } - arg.Call = c - if arg.Type != nil && arg.Type.Name() != typ.Name() { - panic("different type is already assigned: " + arg.Type.Name() + " vs " + typ.Name()) - } - arg.Type = typ - switch arg.Kind { - case ArgPointer: - arg.Dir = DirIn - switch typ1 := typ.(type) { - case sys.PtrType: - if arg.Res != nil { - if err := rec(arg.Res, typ1.Type, ArgDir(typ1.Dir)); err != nil { - return err - } - } - } - case ArgGroup: - arg.Dir = dir - switch typ1 := typ.(type) { - case *sys.StructType: - if len(arg.Inner) != len(typ1.Fields) { - return fmt.Errorf("wrong struct field count: %v, want %v", len(arg.Inner), len(typ1.Fields)) - } - for i, arg1 := range arg.Inner { - if err := rec(arg1, typ1.Fields[i], dir); err != nil { - return err - } - } - case sys.ArrayType: - for _, arg1 := range arg.Inner { - if err := rec(arg1, typ1.Type, dir); err != nil { - return err - } - } - } - case ArgUnion: - arg.Dir = dir - if err := rec(arg.Option, arg.OptionType, dir); err != nil { - return err - } - default: - arg.Dir = dir - } - return nil - } - for i, arg := range c.Args { - if c.Meta == nil { - panic("nil meta") - } - if err := rec(arg, c.Meta.Args[i], DirIn); err != nil { - return err - } - } - if c.Ret == nil { - c.Ret = returnArg() - c.Ret.Call = c - c.Ret.Type = c.Meta.Ret - c.Ret.Dir = DirOut - } - return nil -} - -func generateSize(typ sys.Type, arg *Arg, lenType sys.LenType) *Arg { +func generateSize(arg *Arg, lenType *sys.LenType) *Arg { if arg == nil { // Arg is an optional pointer, set size to 0. - return constArg(0) + return constArg(lenType, 0) } - switch typ.(type) { - case sys.VmaType: - return pageSizeArg(arg.AddrPagesNum, 0) - case sys.ArrayType: + switch arg.Type.(type) { + case *sys.VmaType: + return pageSizeArg(lenType, arg.AddrPagesNum, 0) + case *sys.ArrayType: if lenType.ByteSize { - return constArg(arg.Size(typ)) + return constArg(lenType, arg.Size()) } else { - return constArg(uintptr(len(arg.Inner))) + return constArg(lenType, uintptr(len(arg.Inner))) } default: - return constArg(arg.Size(typ)) + return constArg(lenType, arg.Size()) } } -func assignSizes(types []sys.Type, args []*Arg) { +func assignSizes(args []*Arg) { + // Create a map of args and calculate size of the whole struct. argsMap := make(map[string]*Arg) - typesMap := make(map[string]sys.Type) - - // Create a map of args and types. - for i, typ := range types { - if sys.IsPad(typ) { + var parentSize uintptr + for _, arg := range args { + parentSize += arg.Size() + if sys.IsPad(arg.Type) { continue } - if typ.Name() == "parent" { - panic("parent is reserved len name") - } - - innerArg := args[i].InnerArg(typ) - innerType := typ.InnerType() - - if _, ok := argsMap[typ.Name()]; ok { - panic(fmt.Sprintf("mutiple args with the same name '%v', types: %+v, args: %+v", typ.Name(), types, args)) - } - argsMap[typ.Name()] = innerArg - typesMap[typ.Name()] = innerType - } - - // Calculate size of the whole struct. - var parentSize uintptr - for i, typ := range types { - parentSize += args[i].Size(typ) + argsMap[arg.Type.Name()] = arg } // Fill in size arguments. - for i, typ := range types { - if lenType, ok := typ.InnerType().(sys.LenType); ok { - lenArg := args[i].InnerArg(typ) - if lenArg == nil { - // Pointer to optional len field, no need to fill in value. - continue - } - - if lenType.Buf == "parent" { - *lenArg = *constArg(parentSize) + for _, arg := range args { + if arg = arg.InnerArg(); arg == nil { + continue // Pointer to optional len field, no need to fill in value. + } + if typ, ok := arg.Type.(*sys.LenType); ok { + if typ.Buf == "parent" { + arg.Val = parentSize continue } - arg, ok := argsMap[lenType.Buf] + buf, ok := argsMap[typ.Buf] if !ok { - panic(fmt.Sprintf("len field '%v' references non existent field '%v', argsMap: %+v, typesMap: %+v", - lenType.Name(), lenType.Buf, argsMap, typesMap)) + panic(fmt.Sprintf("len field '%v' references non existent field '%v', argsMap: %+v", + typ.Name(), typ.Buf, argsMap)) } - typ := typesMap[lenType.Buf] - *lenArg = *generateSize(typ, arg, lenType) + *arg = *generateSize(buf.InnerArg(), typ) } } } func assignSizesCall(c *Call) { - var rec func(arg *Arg, typ sys.Type) - rec = func(arg *Arg, typ sys.Type) { - switch arg.Kind { - case ArgPointer: - switch typ1 := typ.(type) { - case sys.PtrType: - if arg.Res != nil { - rec(arg.Res, typ1.Type) - } - } - case ArgGroup: - switch typ1 := typ.(type) { - case *sys.StructType: - if len(arg.Inner) != len(typ1.Fields) { - panic(fmt.Sprintf("wrong struct field count: %v, want %v", len(arg.Inner), len(typ1.Fields))) - } - for i, arg1 := range arg.Inner { - rec(arg1, typ1.Fields[i]) - } - assignSizes(typ1.Fields, arg.Inner) - case sys.ArrayType: - for _, arg1 := range arg.Inner { - rec(arg1, typ1.Type) - } - } - case ArgUnion: - rec(arg.Option, arg.OptionType) + assignSizes(c.Args) + foreachArg(c, func(arg, base *Arg, parent *[]*Arg) { + if _, ok := arg.Type.(*sys.StructType); ok { + assignSizes(arg.Inner) } - } - if c.Meta == nil { - panic("nil meta") - } - for i, arg := range c.Args { - rec(arg, c.Meta.Args[i]) - } - assignSizes(c.Meta.Args, c.Args) + }) } func sanitizeCall(c *Call) { diff --git a/prog/clone.go b/prog/clone.go index 14f9db759..69a54cd4e 100644 --- a/prog/clone.go +++ b/prog/clone.go @@ -24,7 +24,6 @@ func (p *Prog) Clone() *Prog { func (arg *Arg) clone(c *Call, newargs map[*Arg]*Arg) *Arg { arg1 := new(Arg) *arg1 = *arg - arg1.Call = c arg1.Data = append([]byte{}, arg.Data...) switch arg.Kind { case ArgPointer: diff --git a/prog/encoding.go b/prog/encoding.go index 01438f5b9..7afe218be 100644 --- a/prog/encoding.go +++ b/prog/encoding.go @@ -93,7 +93,7 @@ func (a *Arg) serialize(buf io.Writer, vars map[*Arg]int, varSeq *int) { switch a.Type.(type) { case *sys.StructType: delims = []byte{'{', '}'} - case sys.ArrayType: + case *sys.ArrayType: delims = []byte{'[', ']'} default: panic("unknown group type") @@ -137,7 +137,10 @@ func Deserialize(data []byte) (prog *Prog, err error) { if meta == nil { return nil, fmt.Errorf("unknown syscall %v", name) } - c := &Call{Meta: meta} + c := &Call{ + Meta: meta, + Ret: returnArg(meta.Ret), + } prog.Calls = append(prog.Calls, c) p.Parse('(') for i := 0; p.Char() != ')'; i++ { @@ -164,9 +167,6 @@ func Deserialize(data []byte) (prog *Prog, err error) { if len(c.Args) != len(meta.Args) { return nil, fmt.Errorf("wrong call arg count: %v, want %v", len(c.Args), len(meta.Args)) } - if err := assignTypeAndDir(c); err != nil { - return nil, err - } if r != "" { vars[r] = c.Ret } @@ -196,14 +196,14 @@ func parseArg(typ sys.Type, p *parser, vars map[string]*Arg) (*Arg, error) { if err != nil { return nil, fmt.Errorf("wrong arg value '%v': %v", val, err) } - arg = constArg(uintptr(v)) + arg = constArg(typ, 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) + arg = resultArg(typ, v) if p.Char() == '/' { p.Parse('/') op := p.Ident() @@ -225,9 +225,9 @@ func parseArg(typ sys.Type, p *parser, vars map[string]*Arg) (*Arg, error) { case '&': var typ1 sys.Type switch t1 := typ.(type) { - case sys.PtrType: + case *sys.PtrType: typ1 = t1.Type - case sys.VmaType: + case *sys.VmaType: default: return nil, fmt.Errorf("& arg is not a pointer: %#v", typ) } @@ -241,13 +241,13 @@ func parseArg(typ sys.Type, p *parser, vars map[string]*Arg) (*Arg, error) { if err != nil { return nil, err } - arg = pointerArg(page, off, size, inner) + arg = pointerArg(typ, page, off, size, inner) case '(': page, off, _, err := parseAddr(p, false) if err != nil { return nil, err } - arg = pageSizeArg(page, off) + arg = pageSizeArg(typ, page, off) case '"': p.Parse('"') val := "" @@ -259,7 +259,7 @@ func parseArg(typ sys.Type, p *parser, vars map[string]*Arg) (*Arg, error) { if err != nil { return nil, fmt.Errorf("data arg has bad value '%v'", val) } - arg = dataArg(data) + arg = dataArg(typ, data) case '{': t1, ok := typ.(*sys.StructType) if !ok { @@ -273,7 +273,7 @@ func parseArg(typ sys.Type, p *parser, vars map[string]*Arg) (*Arg, error) { } fld := t1.Fields[i] if sys.IsPad(fld) { - inner = append(inner, constArg(0)) + inner = append(inner, constArg(fld, 0)) } else { arg, err := parseArg(fld, p, vars) if err != nil { @@ -286,12 +286,12 @@ func parseArg(typ sys.Type, p *parser, vars map[string]*Arg) (*Arg, error) { } } p.Parse('}') - if sys.IsPad(t1.Fields[len(t1.Fields)-1]) { - inner = append(inner, constArg(0)) + if last := t1.Fields[len(t1.Fields)-1]; sys.IsPad(last) { + inner = append(inner, constArg(last, 0)) } - arg = groupArg(inner) + arg = groupArg(typ, inner) case '[': - t1, ok := typ.(sys.ArrayType) + t1, ok := typ.(*sys.ArrayType) if !ok { return nil, fmt.Errorf("'[' arg is not an array: %#v", typ) } @@ -308,7 +308,7 @@ func parseArg(typ sys.Type, p *parser, vars map[string]*Arg) (*Arg, error) { } } p.Parse(']') - arg = groupArg(inner) + arg = groupArg(typ, inner) case '@': t1, ok := typ.(*sys.UnionType) if !ok { @@ -331,7 +331,7 @@ func parseArg(typ sys.Type, p *parser, vars map[string]*Arg) (*Arg, error) { if err != nil { return nil, err } - arg = unionArg(opt, optType) + arg = unionArg(typ, opt, optType) case 'n': p.Parse('n') p.Parse('i') diff --git a/prog/encodingexec.go b/prog/encodingexec.go index 239ef3514..4c2cd2524 100644 --- a/prog/encodingexec.go +++ b/prog/encodingexec.go @@ -46,7 +46,7 @@ func (p *Prog) SerializeForExec() []byte { w.args[base] = &argInfo{} } w.args[arg] = &argInfo{Offset: w.args[base].CurSize} - w.args[base].CurSize += arg.Size(arg.Type) + w.args[base].CurSize += arg.Size() }) // Generate copyin instructions that fill in data into pointer arguments. foreachArg(c, func(arg, _ *Arg, _ *[]*Arg) { @@ -69,7 +69,7 @@ func (p *Prog) SerializeForExec() []byte { if arg1.Kind == ArgData && len(arg1.Data) == 0 { return } - if arg1.Dir != DirOut { + if arg1.Type.Dir() != sys.DirOut { w.write(ExecInstrCopyin) w.write(physicalAddr(arg) + w.args[arg1].Offset) w.writeArg(arg1) @@ -105,7 +105,7 @@ func (p *Prog) SerializeForExec() []byte { instrSeq++ w.write(ExecInstrCopyout) w.write(physicalAddr(base) + info.Offset) - w.write(arg.Size(arg.Type)) + w.write(arg.Size()) default: panic("bad arg kind in copyout") } @@ -147,21 +147,21 @@ func (w *execContext) writeArg(arg *Arg) { switch arg.Kind { case ArgConst: w.write(ExecArgConst) - w.write(arg.Size(arg.Type)) - w.write(arg.Value(arg.Type)) + w.write(arg.Size()) + w.write(arg.Value()) case ArgResult: w.write(ExecArgResult) - w.write(arg.Size(arg.Type)) + w.write(arg.Size()) w.write(w.args[arg.Res].Idx) w.write(arg.OpDiv) w.write(arg.OpAdd) case ArgPointer: w.write(ExecArgConst) - w.write(arg.Size(arg.Type)) + w.write(arg.Size()) w.write(physicalAddr(arg)) case ArgPageSize: w.write(ExecArgConst) - w.write(arg.Size(arg.Type)) + w.write(arg.Size()) w.write(arg.AddrPage * pageSize) case ArgData: w.write(ExecArgData) diff --git a/prog/mutation.go b/prog/mutation.go index 577e24796..8d5fa36a4 100644 --- a/prog/mutation.go +++ b/prog/mutation.go @@ -57,13 +57,13 @@ func (p *Prog) Mutate(rs rand.Source, ncalls int, ct *ChoiceTable) { if base.Kind != ArgPointer || base.Res == nil { panic("bad base arg") } - baseSize = base.Res.Size(base.Res.Type) + baseSize = base.Res.Size() } switch a := arg.Type.(type) { - case sys.IntType, sys.FlagsType, sys.FileoffType, sys.ResourceType, sys.VmaType: - arg1, calls1 := r.generateArg(s, arg.Type, arg.Dir) - p.replaceArg(arg, arg1, calls1) - case sys.BufferType: + case *sys.IntType, *sys.FlagsType, *sys.ResourceType, *sys.VmaType: + arg1, calls1 := r.generateArg(s, arg.Type) + p.replaceArg(c, arg, arg1, calls1) + case *sys.BufferType: switch a.Kind { case sys.BufferBlobRand, sys.BufferBlobRange: var data []byte @@ -89,23 +89,16 @@ func (p *Prog) Mutate(rs rand.Source, ncalls int, ct *ChoiceTable) { if r.bin() { arg.Data = mutateData(r, append([]byte{}, arg.Data...), int(0), ^int(0)) } else { - arg.Data = r.randString(s) + arg.Data = r.randString(s, a.Values, a.Dir()) } - case sys.BufferFilesystem: - arg.Data = r.filesystem(s) + case sys.BufferFilename: + arg.Data = []byte(r.filename(s)) case sys.BufferSockaddr: arg.Data = r.sockaddr(s) - case sys.BufferAlgType: - arg.Data = r.algType(s) - case sys.BufferAlgName: - arg.Data = r.algName(s) default: panic("unknown buffer kind") } - case sys.FilenameType: - filename := r.filename(s) - arg.Data = []byte(filename) - case sys.ArrayType: + case *sys.ArrayType: count := uintptr(0) switch a.Kind { case sys.ArrayRandLen: @@ -123,7 +116,7 @@ func (p *Prog) Mutate(rs rand.Source, ncalls int, ct *ChoiceTable) { if count > uintptr(len(arg.Inner)) { var calls []*Call for count > uintptr(len(arg.Inner)) { - arg1, calls1 := r.generateArg(s, a.Type, arg.Dir) + arg1, calls1 := r.generateArg(s, a.Type) arg.Inner = append(arg.Inner, arg1) for _, c1 := range calls1 { calls = append(calls, c1) @@ -131,27 +124,25 @@ func (p *Prog) Mutate(rs rand.Source, ncalls int, ct *ChoiceTable) { } } for _, c1 := range calls { - assignTypeAndDir(c1) sanitizeCall(c1) } - assignTypeAndDir(c) sanitizeCall(c) p.insertBefore(c, calls) } else if count < uintptr(len(arg.Inner)) { for _, arg := range arg.Inner[count:] { - p.removeArg(arg) + p.removeArg(c, arg) } arg.Inner = arg.Inner[:count] } // TODO: swap elements of the array - case sys.PtrType: + case *sys.PtrType: // TODO: we don't know size for out args size := uintptr(1) if arg.Res != nil { - size = arg.Res.Size(arg.Res.Type) + size = arg.Res.Size() } - arg1, calls1 := r.addr(s, size, arg.Res) - p.replaceArg(arg, arg1, calls1) + arg1, calls1 := r.addr(s, a, size, arg.Res) + p.replaceArg(c, arg, arg1, calls1) case *sys.StructType: ctor := isSpecialStruct(a) if ctor == nil { @@ -159,7 +150,7 @@ func (p *Prog) Mutate(rs rand.Source, ncalls int, ct *ChoiceTable) { } arg1, calls1 := ctor(r, s) for i, f := range arg1.Inner { - p.replaceArg(arg.Inner[i], f, calls1) + p.replaceArg(c, arg.Inner[i], f, calls1) calls1 = nil } case *sys.UnionType: @@ -167,23 +158,22 @@ func (p *Prog) Mutate(rs rand.Source, ncalls int, ct *ChoiceTable) { for optType.Name() == arg.OptionType.Name() { optType = a.Options[r.Intn(len(a.Options))] } - p.removeArg(arg.Option) - opt, calls := r.generateArg(s, optType, arg.Dir) - arg1 := unionArg(opt, optType) - p.replaceArg(arg, arg1, calls) - case sys.LenType: + p.removeArg(c, arg.Option) + opt, calls := r.generateArg(s, optType) + arg1 := unionArg(a, opt, optType) + p.replaceArg(c, arg, arg1, calls) + case *sys.LenType: panic("bad arg returned by mutationArgs: LenType") - case sys.ConstType, sys.StrConstType: + case *sys.ConstType: panic("bad arg returned by mutationArgs: ConstType") default: panic(fmt.Sprintf("bad arg returned by mutationArgs: %#v, type=%#v", *arg, arg.Type)) } // Update base pointer if size has increased. - if base != nil && baseSize < base.Res.Size(base.Res.Type) { - arg1, calls1 := r.addr(s, base.Res.Size(base.Res.Type), base.Res) + if base != nil && baseSize < base.Res.Size() { + arg1, calls1 := r.addr(s, base.Type, base.Res.Size(), base.Res) for _, c1 := range calls1 { - assignTypeAndDir(c1) sanitizeCall(c1) } p.insertBefore(c, calls1) @@ -194,8 +184,6 @@ func (p *Prog) Mutate(rs rand.Source, ncalls int, ct *ChoiceTable) { // Update all len fields. assignSizesCall(c) - // Assign Arg.Type fields for newly created len args. - assignTypeAndDir(c) } }, 1, func() { @@ -210,7 +198,6 @@ func (p *Prog) Mutate(rs rand.Source, ncalls int, ct *ChoiceTable) { ) } for _, c := range p.Calls { - assignTypeAndDir(c) sanitizeCall(c) } if err := p.validate(); err != nil { @@ -254,18 +241,7 @@ func Minimize(p0 *Prog, callIndex0 int, pred func(*Prog, int) bool) (*Prog, int) } } // Prepend uber-mmap. - mmap := &Call{ - Meta: sys.CallMap["mmap"], - Args: []*Arg{ - pointerArg(0, 0, uintptr(hi)+1, nil), - pageSizeArg(uintptr(hi)+1, 0), - constArg(sys.PROT_READ | sys.PROT_WRITE), - constArg(sys.MAP_ANONYMOUS | sys.MAP_PRIVATE | sys.MAP_FIXED), - constArg(sys.InvalidFD), - constArg(0), - }, - } - assignTypeAndDir(mmap) + mmap := createMmapCall(0, uintptr(hi)+1) p.Calls = append([]*Call{mmap}, p.Calls...) if callIndex != -1 { callIndex++ @@ -332,19 +308,23 @@ func mutationArgs(c *Call) (args, bases []*Arg) { return } // These special structs are mutated as a whole. - case sys.ArrayType: + case *sys.ArrayType: // Don't mutate fixed-size arrays. if typ.Kind == sys.ArrayRangeLen && typ.RangeBegin == typ.RangeEnd { return } - case sys.LenType: + case *sys.LenType: // Size is updated when the size-of arg change. return - case sys.ConstType, sys.StrConstType: + case *sys.ConstType: // Well, this is const. return + case *sys.BufferType: + if typ.Kind == sys.BufferString && len(typ.Values) == 1 { + return // string const + } } - if arg.Dir == DirOut { + if arg.Type.Dir() == sys.DirOut { return } if base != nil { diff --git a/prog/prio.go b/prog/prio.go index 8983c1c92..59a205a5d 100644 --- a/prog/prio.go +++ b/prog/prio.go @@ -50,9 +50,9 @@ func calcStaticPriorities() [][]float32 { uses[id][c.ID] = weight } } - foreachArgType(c, func(t sys.Type, d ArgDir) { + sys.ForeachType(c, func(t sys.Type) { switch a := t.(type) { - case sys.ResourceType: + case *sys.ResourceType: if a.Desc.Name == "pid" || a.Desc.Name == "uid" || a.Desc.Name == "gid" { // Pid/uid/gid usually play auxiliary role, // but massively happen in some structs. @@ -68,34 +68,35 @@ func calcStaticPriorities() [][]float32 { noteUsage(float32(w), str) } } - case sys.PtrType: + case *sys.PtrType: if _, ok := a.Type.(*sys.StructType); ok { noteUsage(1.0, "ptrto-%v", a.Type.Name()) } if _, ok := a.Type.(*sys.UnionType); ok { noteUsage(1.0, "ptrto-%v", a.Type.Name()) } - if arr, ok := a.Type.(sys.ArrayType); ok { + if arr, ok := a.Type.(*sys.ArrayType); ok { noteUsage(1.0, "ptrto-%v", arr.Type.Name()) } - case sys.BufferType: + case *sys.BufferType: switch a.Kind { - case sys.BufferBlobRand, sys.BufferBlobRange, sys.BufferFilesystem, sys.BufferAlgType, sys.BufferAlgName: + case sys.BufferBlobRand, sys.BufferBlobRange: case sys.BufferString: - noteUsage(0.2, "str") + if a.SubKind != "" { + noteUsage(0.2, fmt.Sprintf("str-%v", a.SubKind)) + } case sys.BufferSockaddr: noteUsage(1.0, "sockaddr") + case sys.BufferFilename: + noteUsage(1.0, "filename") default: panic("unknown buffer kind") } - case sys.VmaType: + case *sys.VmaType: noteUsage(0.5, "vma") - case sys.FilenameType: - noteUsage(1.0, "filename") - case sys.IntType: + case *sys.IntType: switch a.Kind { - case sys.IntPlain: - case sys.IntRange: + case sys.IntPlain, sys.IntFileoff, sys.IntRange: case sys.IntSignalno: noteUsage(1.0, "signalno") case sys.IntInaddr: @@ -196,47 +197,6 @@ func normalizePrio(prios [][]float32) { } } -func foreachArgType(meta *sys.Call, f func(sys.Type, ArgDir)) { - seen := make(map[sys.Type]bool) - var rec func(t sys.Type, dir ArgDir) - rec = func(t sys.Type, d ArgDir) { - f(t, d) - switch a := t.(type) { - case sys.ArrayType: - rec(a.Type, d) - case sys.PtrType: - rec(a.Type, ArgDir(a.Dir)) - case *sys.StructType: - if seen[a] { - return // prune recursion via pointers to structs/unions - } - seen[a] = true - for _, f := range a.Fields { - rec(f, d) - } - case *sys.UnionType: - if seen[a] { - return // prune recursion via pointers to structs/unions - } - seen[a] = true - for _, opt := range a.Options { - rec(opt, d) - } - case sys.ResourceType, sys.FileoffType, sys.BufferType, - sys.VmaType, sys.LenType, sys.FlagsType, sys.ConstType, - sys.StrConstType, sys.IntType, sys.FilenameType: - default: - panic("unknown type") - } - } - for _, t := range meta.Args { - rec(t, DirIn) - } - if meta.Ret != nil { - rec(meta.Ret, DirOut) - } -} - // ChooseTable allows to do a weighted choice of a syscall for a given syscall // based on call-to-call priorities and a set of enabled syscalls. type ChoiceTable struct { diff --git a/prog/prog.go b/prog/prog.go index 9953f3ef0..e3a0febd2 100644 --- a/prog/prog.go +++ b/prog/prog.go @@ -20,10 +20,8 @@ type Call struct { } type Arg struct { - Call *Call Type sys.Type Kind ArgKind - Dir ArgDir Val uintptr // value of ArgConst AddrPage uintptr // page index for ArgPointer address, page count for ArgPageSize AddrOffset int // page offset for ArgPointer address @@ -53,26 +51,17 @@ const ( ArgReturn // fake value denoting syscall return value ) -type ArgDir sys.Dir - -const ( - DirIn = ArgDir(sys.DirIn) - DirOut = ArgDir(sys.DirOut) - DirInOut = ArgDir(sys.DirInOut) -) - // Returns inner arg for PtrType args -func (a *Arg) InnerArg(typ sys.Type) *Arg { - switch typ1 := typ.(type) { - case sys.PtrType: +func (a *Arg) InnerArg() *Arg { + switch typ := a.Type.(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)) + if !typ.Optional() { + panic(fmt.Sprintf("non-optional pointer is nil\narg: %+v\ntype: %+v", a, typ)) } + return nil } else { - return a.Res.InnerArg(typ1.Type) + return a.Res.InnerArg() } default: return a @@ -96,43 +85,39 @@ func encodeValue(value, size uintptr, bigEndian bool) uintptr { } // Returns value taking endianness into consideration. -func (a *Arg) Value(typ sys.Type) uintptr { - switch t := typ.(type) { - case sys.IntType: - return encodeValue(a.Val, t.Size(), t.BigEndian) - case sys.ConstType: - return encodeValue(a.Val, t.Size(), t.BigEndian) - case sys.FlagsType: - return encodeValue(a.Val, t.Size(), t.BigEndian) - case sys.LenType: - return encodeValue(a.Val, t.Size(), t.BigEndian) - case sys.FileoffType: - return encodeValue(a.Val, t.Size(), t.BigEndian) +func (a *Arg) Value() uintptr { + switch typ := a.Type.(type) { + case *sys.IntType: + return encodeValue(a.Val, typ.Size(), typ.BigEndian) + case *sys.ConstType: + return encodeValue(a.Val, typ.Size(), typ.BigEndian) + case *sys.FlagsType: + return encodeValue(a.Val, typ.Size(), typ.BigEndian) + case *sys.LenType: + return encodeValue(a.Val, typ.Size(), typ.BigEndian) } return a.Val } -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: +func (a *Arg) Size() uintptr { + switch typ := a.Type.(type) { + case *sys.IntType, *sys.LenType, *sys.FlagsType, *sys.ConstType, + *sys.ResourceType, *sys.VmaType, *sys.PtrType: return typ.Size() - case sys.FilenameType: - return uintptr(len(a.Data)) - case sys.BufferType: + 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) + for _, fld := range a.Inner { + size += fld.Size() } return size case *sys.UnionType: - return a.Option.Size(a.OptionType) - case sys.ArrayType: + return a.Option.Size() + case *sys.ArrayType: var size uintptr for _, in := range a.Inner { - size += in.Size(typ1.Type) + size += in.Size() } return size default: @@ -140,12 +125,12 @@ func (a *Arg) Size(typ sys.Type) uintptr { } } -func constArg(v uintptr) *Arg { - return &Arg{Kind: ArgConst, Val: v} +func constArg(t sys.Type, v uintptr) *Arg { + return &Arg{Type: t, Kind: ArgConst, Val: v} } -func resultArg(r *Arg) *Arg { - arg := &Arg{Kind: ArgResult, Res: r} +func resultArg(t sys.Type, r *Arg) *Arg { + arg := &Arg{Type: t, Kind: ArgResult, Res: r} if r.Uses == nil { r.Uses = make(map[*Arg]bool) } @@ -156,28 +141,28 @@ func resultArg(r *Arg) *Arg { return arg } -func dataArg(data []byte) *Arg { - return &Arg{Kind: ArgData, Data: append([]byte{}, data...)} +func dataArg(t sys.Type, data []byte) *Arg { + return &Arg{Type: t, Kind: ArgData, Data: append([]byte{}, data...)} } -func pointerArg(page uintptr, off int, npages uintptr, obj *Arg) *Arg { - return &Arg{Kind: ArgPointer, AddrPage: page, AddrOffset: off, AddrPagesNum: npages, Res: obj} +func pointerArg(t sys.Type, page uintptr, off int, npages uintptr, obj *Arg) *Arg { + return &Arg{Type: t, Kind: ArgPointer, AddrPage: page, AddrOffset: off, AddrPagesNum: npages, Res: obj} } -func pageSizeArg(npages uintptr, off int) *Arg { - return &Arg{Kind: ArgPageSize, AddrPage: npages, AddrOffset: off} +func pageSizeArg(t sys.Type, npages uintptr, off int) *Arg { + return &Arg{Type: t, Kind: ArgPageSize, AddrPage: npages, AddrOffset: off} } -func groupArg(inner []*Arg) *Arg { - return &Arg{Kind: ArgGroup, Inner: inner} +func groupArg(t sys.Type, inner []*Arg) *Arg { + return &Arg{Type: t, Kind: ArgGroup, Inner: inner} } -func unionArg(opt *Arg, typ sys.Type) *Arg { - return &Arg{Kind: ArgUnion, Option: opt, OptionType: typ} +func unionArg(t sys.Type, opt *Arg, typ sys.Type) *Arg { + return &Arg{Type: t, Kind: ArgUnion, Option: opt, OptionType: typ} } -func returnArg() *Arg { - return &Arg{Kind: ArgReturn, Dir: DirOut} +func returnArg(t sys.Type) *Arg { + return &Arg{Type: t, Kind: ArgReturn} } func (p *Prog) insertBefore(c *Call, calls []*Call) { @@ -197,8 +182,8 @@ func (p *Prog) insertBefore(c *Call, calls []*Call) { p.Calls = newCalls } -// replaceArg replaces arg with arg1 in p, and inserts calls before arg call. -func (p *Prog) replaceArg(arg, arg1 *Arg, calls []*Call) { +// replaceArg replaces arg with arg1 in call c in program p, and inserts calls before arg call. +func (p *Prog) replaceArg(c *Call, arg, arg1 *Arg, calls []*Call) { if arg.Kind != ArgConst && arg.Kind != ArgResult && arg.Kind != ArgPointer && arg.Kind != ArgUnion { panic(fmt.Sprintf("replaceArg: bad arg kind %v", arg.Kind)) } @@ -209,10 +194,8 @@ func (p *Prog) replaceArg(arg, arg1 *Arg, calls []*Call) { delete(arg.Res.Uses, arg) } for _, c := range calls { - assignTypeAndDir(c) sanitizeCall(c) } - c := arg.Call p.insertBefore(c, calls) // Somewhat hacky, but safe and preserves references to arg. uses := arg.Uses @@ -222,12 +205,11 @@ func (p *Prog) replaceArg(arg, arg1 *Arg, calls []*Call) { delete(arg.Res.Uses, arg1) arg.Res.Uses[arg] = true } - assignTypeAndDir(c) sanitizeCall(c) } -// removeArg removes all references to/from arg0 from p. -func (p *Prog) removeArg(arg0 *Arg) { +// removeArg removes all references to/from arg0 of call c from p. +func (p *Prog) removeArg(c *Call, arg0 *Arg) { foreachSubarg(arg0, func(arg, _ *Arg, _ *[]*Arg) { if arg.Kind == ArgResult { if _, ok := arg.Res.Uses[arg]; !ok { @@ -239,8 +221,8 @@ func (p *Prog) removeArg(arg0 *Arg) { if arg1.Kind != ArgResult { panic("use references not ArgResult") } - arg2 := constArg(arg1.Type.Default()) - p.replaceArg(arg1, arg2, nil) + arg2 := constArg(arg1.Type, arg1.Type.Default()) + p.replaceArg(c, arg1, arg2, nil) } }) } @@ -251,7 +233,7 @@ func (p *Prog) removeCall(idx int) { copy(p.Calls[idx:], p.Calls[idx+1:]) p.Calls = p.Calls[:len(p.Calls)-1] for _, arg := range c.Args { - p.removeArg(arg) + p.removeArg(c, arg) } - p.removeArg(c.Ret) + p.removeArg(c, c.Ret) } diff --git a/prog/rand.go b/prog/rand.go index 18dceeaea..eb24dcc4d 100644 --- a/prog/rand.go +++ b/prog/rand.go @@ -193,25 +193,25 @@ func (r *randGen) inport(s *state) uint16 { return uint16(r.Intn(20))<<8 + 0xab } -func (r *randGen) in6addr(s *state) (arg *Arg, calls []*Call) { +func (r *randGen) in6addr(s *state, typ sys.Type) (arg *Arg, calls []*Call) { // addr: loopback (big endian) - return groupArg([]*Arg{ - constArg(0), - constArg(0), - constArg(0), - constArg(1 << 24), + return groupArg(typ, []*Arg{ + constArg(nil, 0), + constArg(nil, 0), + constArg(nil, 0), + constArg(nil, 1<<24), }), nil } -func (r *randGen) inaddrany(s *state) (arg *Arg, calls []*Call) { +func (r *randGen) inaddrany(s *state, typ sys.Type) (arg *Arg, calls []*Call) { if r.bin() { - return r.in6addr(s) + return r.in6addr(s, typ) } else { - return groupArg([]*Arg{ - constArg(uintptr(r.inaddr(s))), - constArg(0), - constArg(0), - constArg(0), + return groupArg(typ, []*Arg{ + constArg(nil, uintptr(r.inaddr(s))), + constArg(nil, 0), + constArg(nil, 0), + constArg(nil, 0), }), nil } } @@ -258,7 +258,20 @@ func (r *randGen) sockaddr(s *state) []byte { return data } -func (r *randGen) randString(s *state) []byte { +func (r *randGen) randString(s *state, vals []string, dir sys.Dir) []byte { + data := r.randStringImpl(s, vals) + if dir == sys.DirOut { + for i := range data { + data[i] = 0 + } + } + return data +} + +func (r *randGen) randStringImpl(s *state, vals []string) []byte { + if len(vals) != 0 { + return []byte(vals[r.Intn(len(vals))]) + } if len(s.strings) != 0 && r.bin() { // Return an existing string. strings := make([]string, 0, len(s.strings)) @@ -288,81 +301,43 @@ func (r *randGen) randString(s *state) []byte { return buf.Bytes() } -func (r *randGen) filesystem(s *state) []byte { - dict := []string{"sysfs", "rootfs", "ramfs", "tmpfs", "devtmpfs", "debugfs", - "securityfs", "sockfs", "pipefs", "anon_inodefs", "devpts", "ext3", "ext2", "ext4", - "hugetlbfs", "vfat", "ecryptfs", "kdbusfs", "fuseblk", "fuse", "rpc_pipefs", - "nfs", "nfs4", "nfsd", "binfmt_misc", "autofs", "xfs", "jfs", "msdos", "ntfs", - "minix", "hfs", "hfsplus", "qnx4", "ufs", "btrfs", "configfs", "ncpfs", "qnx6", - "exofs", "befs", "vxfs", "gfs2", "gfs2meta", "fusectl", "bfs", "nsfs", "efs", - "cifs", "efivarfs", "affs", "tracefs", "bdev", "ocfs2", "ocfs2_dlmfs", "hpfs", - "proc", "afs", "reiserfs", "jffs2", "romfs", "aio", "sysv", "v7", "udf", - "ceph", "pstore", "adfs", "9p", "hostfs", "squashfs", "cramfs", "iso9660", - "coda", "nilfs2", "logfs", "overlay", "f2fs", "omfs", "ubifs", "openpromfs"} - return []byte(dict[r.Intn(len(dict))] + "\x00") -} - -func (r *randGen) algType(s *state) []byte { - dict := []string{"aead", "hash", "rng", "skcipher"} - res := make([]byte, 14) - copy(res, dict[r.Intn(len(dict))]) - return res -} - -func (r *randGen) algName(s *state) []byte { - dict := []string{"cmac(aes)", "ecb(aes)", "cbc(aes)", "hmac(sha1)", "pcbc(fcrypt)", "ghash", - "jitterentropy_rng", "stdrng", "stdrng", "stdrng", "stdrng", "hmac(sha256)", "stdrng", - "stdrng", "stdrng", "stdrng", "stdrng", "842", "lz4hc", "lz4", "lzo", "crct10dif", "crc32", - "crc32c", "michael_mic", "zlib", "deflate", "poly1305", "chacha20", "salsa20", "seed", - "anubis", "khazad", "xeta", "xtea", "tea", "ecb(arc4)", "arc4", "cast6", "cast5", "camellia", - "aes", "tnepres", "serpent", "twofish", "blowfish", "fcrypt", "des3_ede", "des", "tgr128", - "tgr160", "tgr192", "wp256", "wp384", "wp512", "sha384", "sha512", "sha224", "sha256", - "sha1", "rmd320", "rmd256", "rmd160", "rmd128", "md5", "md4", "digest_null", "compress_null", - "ecb(cipher_null)", "cipher_null", "rsa", "poly1305", "xts(serpent)", "lrw(serpent)", - "ctr(serpent)", "cbc(serpent)", "__ecb-serpent-sse2", "ecb(serpent)", "__xts-serpent-sse2", - "__lrw-serpent-sse2", "__ctr-serpent-sse2", "__cbc-serpent-sse2", "__ecb-serpent-sse2", - "salsa20", "xts(twofish)", "lrw(twofish)", "ctr(twofish)", "cbc(twofish)", "ecb(twofish)", - "twofish", "ctr(blowfish)", "cbc(blowfish)", "ecb(blowfish)", "blowfish", "xts(camellia)", - "lrw(camellia)", "ctr(camellia)", "cbc(camellia)", "ecb(camellia)", "camellia", "ctr(des3_ede)", - "cbc(des3_ede)", "ecb(des3_ede)", "des3_ede", "aes"} - res := make([]byte, 64) - copy(res, dict[r.Intn(len(dict))]) - return res -} - func isSpecialStruct(typ sys.Type) func(r *randGen, s *state) (*Arg, []*Call) { - if _, ok := typ.(*sys.StructType); !ok { + 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, false) + return r.timespec(s, a, false) } case "timeval": return func(r *randGen, s *state) (*Arg, []*Call) { - return r.timespec(s, true) + return r.timespec(s, a, true) } case "in6_addr": return func(r *randGen, s *state) (*Arg, []*Call) { - return r.in6addr(s) + return r.in6addr(s, a) } case "in_addr_any": return func(r *randGen, s *state) (*Arg, []*Call) { - return r.inaddrany(s) + return r.inaddrany(s, a) } } return nil } -func (r *randGen) timespec(s *state, usec bool) (arg *Arg, calls []*Call) { +func (r *randGen) timespec(s *state, typ *sys.StructType, usec bool) (arg *Arg, calls []*Call) { // We need to generate timespec/timeval that are either (1) definitely in the past, // or (2) definitely in unreachable fututre, or (3) few ms ahead of now. // Note timespec/timeval can be absolute or relative to now. r.choose( 1, func() { // now for relative, past for absolute - arg = groupArg([]*Arg{constArg(0), constArg(0)}) + arg = groupArg(typ, []*Arg{ + constArg(typ.Fields[0], 0), + constArg(typ.Fields[1], 0), + }) }, 1, func() { // few ms ahead for relative, past for absolute @@ -370,46 +345,77 @@ func (r *randGen) timespec(s *state, usec bool) (arg *Arg, calls []*Call) { if usec { nsec /= 1e3 } - arg = groupArg([]*Arg{constArg(0), constArg(nsec)}) + arg = groupArg(typ, []*Arg{ + constArg(typ.Fields[0], 0), + constArg(typ.Fields[1], nsec), + }) }, 1, func() { // unreachable fututre for both relative and absolute - arg = groupArg([]*Arg{constArg(2e9), constArg(0)}) + arg = groupArg(typ, []*Arg{ + constArg(typ.Fields[0], 2e9), + constArg(typ.Fields[1], 0), + }) }, 1, func() { // few ms ahead for absolute - tp := groupArg([]*Arg{constArg(0), constArg(0)}) + meta := sys.CallMap["clock_gettime"] + ptrArgType := meta.Args[1].(*sys.PtrType) + argType := ptrArgType.Type.(*sys.StructType) + tp := groupArg(argType, []*Arg{ + constArg(argType.Fields[0], 0), + constArg(argType.Fields[1], 0), + }) var tpaddr *Arg - tpaddr, calls = r.addr(s, 2*ptrSize, tp) + tpaddr, calls = r.addr(s, ptrArgType, 2*ptrSize, tp) gettime := &Call{ - Meta: sys.CallMap["clock_gettime"], + Meta: meta, Args: []*Arg{ - constArg(sys.CLOCK_REALTIME), + constArg(meta.Args[0], sys.CLOCK_REALTIME), tpaddr, }, + Ret: returnArg(meta.Ret), } calls = append(calls, gettime) - sec := resultArg(tp.Inner[0]) - nsec := resultArg(tp.Inner[1]) + sec := resultArg(typ.Fields[0], tp.Inner[0]) + nsec := resultArg(typ.Fields[1], tp.Inner[1]) if usec { nsec.OpDiv = 1e3 nsec.OpAdd = 10 * 1e3 } else { nsec.OpAdd = 10 * 1e6 } - arg = groupArg([]*Arg{sec, nsec}) + arg = groupArg(typ, []*Arg{sec, nsec}) }, ) return } -func (r *randGen) addr1(s *state, size uintptr, data *Arg) (*Arg, []*Call) { +// createMmapCall creates a "normal" mmap call that maps [start, start+npages) page range. +func createMmapCall(start, npages uintptr) *Call { + meta := sys.CallMap["mmap"] + mmap := &Call{ + Meta: meta, + Args: []*Arg{ + pointerArg(meta.Args[0], start, 0, npages, nil), + pageSizeArg(meta.Args[1], npages, 0), + constArg(meta.Args[2], sys.PROT_READ|sys.PROT_WRITE), + constArg(meta.Args[3], sys.MAP_ANONYMOUS|sys.MAP_PRIVATE|sys.MAP_FIXED), + constArg(meta.Args[4], sys.InvalidFD), + constArg(meta.Args[5], 0), + }, + Ret: returnArg(meta.Ret), + } + return mmap +} + +func (r *randGen) addr1(s *state, typ sys.Type, size uintptr, data *Arg) (*Arg, []*Call) { npages := (size + pageSize - 1) / pageSize if npages == 0 { npages = 1 } if r.oneOf(10) { - return r.randPageAddr(s, npages, data, false), nil + return r.randPageAddr(s, typ, npages, data, false), nil } for i := uintptr(0); i < maxPages-npages; i++ { free := true @@ -422,24 +428,14 @@ func (r *randGen) addr1(s *state, size uintptr, data *Arg) (*Arg, []*Call) { if !free { continue } - c := &Call{ - Meta: sys.CallMap["mmap"], - Args: []*Arg{ - pointerArg(i, 0, npages, nil), - pageSizeArg(npages, 0), - constArg(sys.PROT_READ | sys.PROT_WRITE), - constArg(sys.MAP_ANONYMOUS | sys.MAP_PRIVATE | sys.MAP_FIXED), - constArg(sys.InvalidFD), - constArg(0), - }, - } - return pointerArg(i, 0, 0, data), []*Call{c} + c := createMmapCall(i, npages) + return pointerArg(typ, i, 0, 0, data), []*Call{c} } - return r.randPageAddr(s, npages, data, false), nil + return r.randPageAddr(s, typ, npages, data, false), nil } -func (r *randGen) addr(s *state, size uintptr, data *Arg) (*Arg, []*Call) { - arg, calls := r.addr1(s, size, data) +func (r *randGen) addr(s *state, typ sys.Type, size uintptr, data *Arg) (*Arg, []*Call) { + arg, calls := r.addr1(s, typ, size, data) if arg.Kind != ArgPointer { panic("bad") } @@ -457,7 +453,7 @@ func (r *randGen) addr(s *state, size uintptr, data *Arg) (*Arg, []*Call) { return arg, calls } -func (r *randGen) randPageAddr(s *state, npages uintptr, data *Arg, vma bool) *Arg { +func (r *randGen) randPageAddr(s *state, typ sys.Type, npages uintptr, data *Arg, vma bool) *Arg { var starts []uintptr for i := uintptr(0); i < maxPages-npages; i++ { busy := true @@ -483,13 +479,13 @@ func (r *randGen) randPageAddr(s *state, npages uintptr, data *Arg, vma bool) *A if !vma { npages = 0 } - return pointerArg(page, 0, npages, data) + return pointerArg(typ, page, 0, npages, data) } -func (r *randGen) createResource(s *state, res sys.ResourceType) (arg *Arg, calls []*Call) { +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 + return constArg(res, special[r.Intn(len(special))]), nil } r.inCreateResource = true defer func() { r.inCreateResource = false }() @@ -516,7 +512,7 @@ func (r *randGen) createResource(s *state, res sys.ResourceType) (arg *Arg, call metas = append(metas, meta) } if len(metas) == 0 { - return constArg(res.Default()), nil + return constArg(res, res.Default()), nil } // Now we have a set of candidate calls that can create the necessary resource. @@ -535,7 +531,7 @@ func (r *randGen) createResource(s *state, res sys.ResourceType) (arg *Arg, call } if len(allres) != 0 { // Bingo! - arg := resultArg(allres[r.Intn(len(allres))]) + arg := resultArg(res, allres[r.Intn(len(allres))]) return arg, calls } switch meta.Name { @@ -599,23 +595,25 @@ func (r *randGen) generateCall(s *state, p *Prog) []*Call { } func (r *randGen) generateParticularCall(s *state, meta *sys.Call) (calls []*Call) { - c := &Call{Meta: meta} - c.Args, calls = r.generateArgs(s, meta.Args, DirIn) + c := &Call{ + Meta: meta, + Ret: returnArg(meta.Ret), + } + c.Args, calls = r.generateArgs(s, meta.Args) calls = append(calls, c) for _, c1 := range calls { - assignTypeAndDir(c1) sanitizeCall(c1) } return calls } -func (r *randGen) generateArgs(s *state, types []sys.Type, dir ArgDir) ([]*Arg, []*Call) { +func (r *randGen) generateArgs(s *state, types []sys.Type) ([]*Arg, []*Call) { var calls []*Call args := make([]*Arg, len(types)) // Generate all args. Size args have the default value 0 for now. for i, typ := range types { - arg, calls1 := r.generateArg(s, typ, dir) + arg, calls1 := r.generateArg(s, typ) if arg == nil { panic(fmt.Sprintf("generated arg is nil for type '%v', types: %+v", typ.Name(), types)) } @@ -623,36 +621,37 @@ func (r *randGen) generateArgs(s *state, types []sys.Type, dir ArgDir) ([]*Arg, calls = append(calls, calls1...) } - assignSizes(types, args) + assignSizes(args) return args, calls } -func (r *randGen) generateArg(s *state, typ sys.Type, dir ArgDir) (arg *Arg, calls []*Call) { - if dir == DirOut { +func (r *randGen) generateArg(s *state, typ sys.Type) (arg *Arg, calls []*Call) { + if typ.Dir() == sys.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 + case *sys.IntType, *sys.FlagsType, *sys.ConstType, + *sys.ResourceType, *sys.VmaType: + return constArg(typ, 0), nil } } if typ.Optional() && r.oneOf(5) { - if _, ok := typ.(sys.BufferType); ok { + if _, ok := typ.(*sys.BufferType); ok { panic("impossible") // parent PtrType must be Optional instead } - return constArg(typ.Default()), nil + return constArg(typ, typ.Default()), nil } switch a := typ.(type) { - case sys.ResourceType: + case *sys.ResourceType: r.choose( 1, func() { special := a.SpecialValues() - arg = constArg(special[r.Intn(len(special))]) + arg = constArg(a, special[r.Intn(len(special))]) }, 90, func() { // Get an existing resource. @@ -664,7 +663,7 @@ func (r *randGen) generateArg(s *state, typ sys.Type, dir ArgDir) (arg *Arg, cal } } if len(allres) != 0 { - arg = resultArg(allres[r.Intn(len(allres))]) + arg = resultArg(a, allres[r.Intn(len(allres))]) } else { arg, calls = r.createResource(s, a) } @@ -675,16 +674,7 @@ func (r *randGen) generateArg(s *state, typ sys.Type, dir ArgDir) (arg *Arg, cal }, ) 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: + case *sys.BufferType: switch a.Kind { case sys.BufferBlobRand, sys.BufferBlobRange: sz := r.randBufLen() @@ -692,56 +682,38 @@ func (r *randGen) generateArg(s *state, typ sys.Type, dir ArgDir) (arg *Arg, cal sz = r.randRange(int(a.RangeBegin), int(a.RangeEnd)) } data := make([]byte, sz) - if dir != DirOut { + if a.Dir() != sys.DirOut { for i := range data { data[i] = byte(r.Intn(256)) } } - return dataArg(data), nil + return dataArg(a, data), nil case sys.BufferString: - data := r.randString(s) - return dataArg(data), nil - case sys.BufferFilesystem: - data := r.filesystem(s) - return dataArg(data), nil + data := r.randString(s, a.Values, a.Dir()) + return dataArg(a, data), nil + case sys.BufferFilename: + filename := r.filename(s) + return dataArg(a, []byte(filename)), 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 { + if a.Dir() == sys.DirOut { for i := range data { data[i] = 0 } } - return dataArg(data), nil + return dataArg(a, data), nil default: panic("unknown buffer kind") } - case sys.VmaType: + case *sys.VmaType: npages := r.randPageCount() - arg := r.randPageAddr(s, npages, nil, true) + arg := r.randPageAddr(s, a, 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: + case *sys.FlagsType: + return constArg(a, r.flags(a.Vals)), nil + case *sys.ConstType: + return constArg(a, a.Val), nil + case *sys.IntType: v := r.randInt() switch a.Kind { case sys.IntSignalno: @@ -750,14 +722,17 @@ func (r *randGen) generateArg(s *state, typ sys.Type, dir ArgDir) (arg *Arg, cal v = uintptr(r.inaddr(s)) case sys.IntInport: v = uintptr(r.inport(s)) + case sys.IntFileoff: + r.choose( + 90, func() { v = 0 }, + 10, func() { v = r.rand(100) }, + 1, func() { v = r.randInt() }, + ) 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: + return constArg(a, v), nil + case *sys.ArrayType: count := uintptr(0) switch a.Kind { case sys.ArrayRandLen: @@ -768,28 +743,28 @@ func (r *randGen) generateArg(s *state, typ sys.Type, dir ArgDir) (arg *Arg, cal var inner []*Arg var calls []*Call for i := uintptr(0); i < count; i++ { - arg1, calls1 := r.generateArg(s, a.Type, dir) + arg1, calls1 := r.generateArg(s, a.Type) inner = append(inner, arg1) calls = append(calls, calls1...) } - return groupArg(inner), calls + return groupArg(a, inner), calls case *sys.StructType: - if ctor := isSpecialStruct(a); ctor != nil && dir != DirOut { + if ctor := isSpecialStruct(a); ctor != nil && a.Dir() != sys.DirOut { arg, calls = ctor(r, s) return } - args, calls := r.generateArgs(s, a.Fields, dir) - group := groupArg(args) + args, calls := r.generateArgs(s, a.Fields) + group := groupArg(a, 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 { + opt, calls := r.generateArg(s, optType) + return unionArg(a, opt, optType), calls + case *sys.PtrType: + inner, calls := r.generateArg(s, a.Type) + if a.Dir() == sys.DirOut && inner == nil { // No data, but we should have got size. - arg, calls1 := r.addr(s, inner.Size(a.Type), nil) + arg, calls1 := r.addr(s, a, inner.Size(), nil) calls = append(calls, calls1...) return arg, calls } @@ -798,15 +773,15 @@ func (r *randGen) generateArg(s *state, typ sys.Type, dir ArgDir) (arg *Arg, cal // 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) + arg = pointerArg(a, addr.AddrPage, addr.AddrOffset, addr.AddrPagesNum, inner) return arg, calls } - arg, calls1 := r.addr(s, inner.Size(a.Type), inner) + arg, calls1 := r.addr(s, a, inner.Size(), inner) calls = append(calls, calls1...) return arg, calls - case sys.LenType: + case *sys.LenType: // Return placeholder value of 0 while generating len args. - return constArg(0), nil + return constArg(a, 0), nil default: panic("unknown argument type") } diff --git a/prog/size_test.go b/prog/size_test.go index 41068e713..ad1ba8740 100644 --- a/prog/size_test.go +++ b/prog/size_test.go @@ -16,7 +16,6 @@ func TestAssignSizeRandom(t *testing.T) { data0 := p.Serialize() for _, call := range p.Calls { assignSizesCall(call) - assignTypeAndDir(call) } if data1 := p.Serialize(); !bytes.Equal(data0, data1) { t.Fatalf("different lens assigned, initial: %v, new: %v", data0, data1) @@ -26,7 +25,6 @@ func TestAssignSizeRandom(t *testing.T) { data0 := p.Serialize() for _, call := range p.Calls { assignSizesCall(call) - assignTypeAndDir(call) } if data1 := p.Serialize(); !bytes.Equal(data0, data1) { t.Fatalf("different lens assigned, initial: %v, new: %v", data0, data1) @@ -113,7 +111,6 @@ func TestAssignSize(t *testing.T) { } for _, call := range p.Calls { assignSizesCall(call) - assignTypeAndDir(call) } p1 := strings.TrimSpace(string(p.Serialize())) if p1 != test.sizedProg { diff --git a/prog/validation.go b/prog/validation.go index a1b68ba1b..701d0ce3d 100644 --- a/prog/validation.go +++ b/prog/validation.go @@ -41,9 +41,6 @@ func (c *Call) validate(ctx *validCtx) error { if arg == nil { return fmt.Errorf("syscall %v: nil arg", c.Meta.Name) } - if arg.Call != c { - return fmt.Errorf("syscall %v: arg has wrong call, call=%p, arg=%+v", c.Meta.Name, c, *arg) - } if ctx.args[arg] { return fmt.Errorf("syscall %v: arg is referenced several times in the tree", c.Meta.Name) } @@ -55,9 +52,9 @@ func (c *Call) validate(ctx *validCtx) error { return fmt.Errorf("syscall %v: no type", c.Meta.Name) } if arg.Type.Name() != typ.Name() { - return fmt.Errorf("syscall %v: arg '%v' type mismatch", c.Meta.Name, typ.Name()) + return fmt.Errorf("syscall %v: type name mismatch: %v vs %v", c.Meta.Name, arg.Type.Name(), typ.Name()) } - if arg.Dir == DirOut { + if arg.Type.Dir() == sys.DirOut { if arg.Val != 0 || arg.AddrPage != 0 || arg.AddrOffset != 0 { return fmt.Errorf("syscall %v: output arg '%v' has data", c.Meta.Name, typ.Name()) } @@ -68,23 +65,17 @@ func (c *Call) validate(ctx *validCtx) error { } } switch arg.Type.(type) { - case sys.ResourceType: + case *sys.ResourceType: switch arg.Kind { case ArgResult: case ArgReturn: case ArgConst: - if arg.Dir == DirOut && arg.Val != 0 { + if arg.Type.Dir() == sys.DirOut && arg.Val != 0 { return fmt.Errorf("syscall %v: out resource arg '%v' has bad const value %v", c.Meta.Name, typ.Name(), arg.Val) } default: return fmt.Errorf("syscall %v: fd arg '%v' has bad kind %v", c.Meta.Name, typ.Name(), arg.Kind) } - case sys.FilenameType: - switch arg.Kind { - case ArgData: - default: - return fmt.Errorf("syscall %v: filename arg '%v' has bad kind %v", c.Meta.Name, typ.Name(), arg.Kind) - } case *sys.StructType, *sys.ArrayType: switch arg.Kind { case ArgGroup: @@ -105,25 +96,25 @@ func (c *Call) validate(ctx *validCtx) error { return fmt.Errorf("syscall %v: result arg '%v' has no reference", c.Meta.Name, typ.Name()) } if !ctx.args[arg.Res] { - return fmt.Errorf("syscall %v: result arg '%v' references out-of-tree result: %p%+v -> %v %p%+v", - c.Meta.Name, typ.Name(), arg, arg, arg.Res.Call.Meta.Name, arg.Res, arg.Res) + return fmt.Errorf("syscall %v: result arg '%v' references out-of-tree result: %p%+v -> %p%+v", + c.Meta.Name, typ.Name(), arg, arg, arg.Res, arg.Res) } if _, ok := arg.Res.Uses[arg]; !ok { return fmt.Errorf("syscall %v: result arg '%v' has broken link (%+v)", c.Meta.Name, typ.Name(), arg.Res.Uses) } case ArgPointer: - if arg.Dir != DirIn { - return fmt.Errorf("syscall %v: pointer arg '%v' has output direction", c.Meta.Name, typ.Name()) - } switch typ1 := typ.(type) { - case sys.VmaType: + case *sys.VmaType: if arg.Res != nil { return fmt.Errorf("syscall %v: vma arg '%v' has data", c.Meta.Name, typ.Name()) } if arg.AddrPagesNum == 0 { return fmt.Errorf("syscall %v: vma arg '%v' has size 0", c.Meta.Name, typ.Name()) } - case sys.PtrType: + case *sys.PtrType: + if arg.Type.Dir() != sys.DirIn { + return fmt.Errorf("syscall %v: pointer arg '%v' has output direction", c.Meta.Name, typ.Name()) + } if arg.Res == nil && !typ.Optional() { return fmt.Errorf("syscall %v: non optional pointer arg '%v' is nil", c.Meta.Name, typ.Name()) } @@ -151,7 +142,7 @@ func (c *Call) validate(ctx *validCtx) error { return err } } - case sys.ArrayType: + case *sys.ArrayType: for _, arg1 := range arg.Inner { if err := checkArg(arg1, typ1.Type); err != nil { return err @@ -185,7 +176,7 @@ func (c *Call) validate(ctx *validCtx) error { return nil } for i, arg := range c.Args { - if c.Ret.Kind != ArgReturn { + if arg.Kind == ArgReturn { return fmt.Errorf("syscall %v: arg '%v' has wrong return kind", c.Meta.Name, arg.Type.Name()) } if err := checkArg(arg, c.Meta.Args[i]); err != nil { |
