diff options
| author | Andrey Konovalov <andreyknvl@gmail.com> | 2016-10-11 20:12:15 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2016-10-11 20:12:15 +0200 |
| commit | cb8fcaf84c7386b129ef54d9ee5fd36b6c56bf0d (patch) | |
| tree | 5ce14a1f0391e34fb97fd317e47ff9a799ccf79e | |
| parent | 0c1a91b184c3cb0ae4d5d7927ad51c5cde958b22 (diff) | |
| parent | 2392578fe9881369b980a20fd6f169471b50a565 (diff) | |
Merge pull request #81 from xairy/better-len
Refactor & improve len type handling
| -rw-r--r-- | prog/analysis.go | 117 | ||||
| -rw-r--r-- | prog/encoding.go | 35 | ||||
| -rw-r--r-- | prog/mutation.go | 60 | ||||
| -rw-r--r-- | prog/mutation_test.go | 32 | ||||
| -rw-r--r-- | prog/prog.go | 50 | ||||
| -rw-r--r-- | prog/rand.go | 136 | ||||
| -rw-r--r-- | prog/size_test.go | 123 | ||||
| -rw-r--r-- | prog/validation.go | 6 | ||||
| -rw-r--r-- | sys/README.md | 48 | ||||
| -rw-r--r-- | sys/decl.go | 57 | ||||
| -rw-r--r-- | sys/fuse.txt | 12 | ||||
| -rw-r--r-- | sys/kdbus.txt | 34 | ||||
| -rw-r--r-- | sys/kvm.txt | 4 | ||||
| -rw-r--r-- | sys/sndcontrol.txt | 4 | ||||
| -rw-r--r-- | sys/sndseq.txt | 8 | ||||
| -rw-r--r-- | sys/sndtimer.txt | 8 | ||||
| -rw-r--r-- | sys/sys.txt | 19 | ||||
| -rw-r--r-- | sys/test.txt | 90 | ||||
| -rw-r--r-- | sysgen/sysgen.go | 122 | ||||
| -rw-r--r-- | sysparser/lexer.go | 19 |
20 files changed, 698 insertions, 286 deletions
diff --git a/prog/analysis.go b/prog/analysis.go index bbda7cb3b..baf87fbb5 100644 --- a/prog/analysis.go +++ b/prog/analysis.go @@ -103,7 +103,8 @@ func (s *state) addressable(addr, size *Arg, ok bool) { n++ } if addr.AddrPage+n > uintptr(len(s.pages)) { - panic(fmt.Sprintf("address is out of bounds: page=%v len=%v (%v, %v) bound=%v", addr.AddrPage, n, size.AddrPage, size.AddrOffset, len(s.pages))) + panic(fmt.Sprintf("address is out of bounds: page=%v len=%v (%v, %v) bound=%v, addr: %+v, size: %+v", + addr.AddrPage, n, size.AddrPage, size.AddrOffset, len(s.pages), addr, size)) } for i := uintptr(0); i < n; i++ { s.pages[addr.AddrPage+i] = ok @@ -216,6 +217,120 @@ func assignTypeAndDir(c *Call) error { return nil } +func generateSize(typ sys.Type, arg *Arg, lenType sys.LenType) *Arg { + if arg == nil { + // Arg is an optional pointer, set size to 0. + return constArg(0) + } + + switch typ.(type) { + case sys.VmaType: + return pageSizeArg(arg.AddrPagesNum, 0) + case sys.ArrayType: + if lenType.ByteSize { + return constArg(arg.Size(typ)) + } else { + return constArg(uintptr(len(arg.Inner))) + } + default: + return constArg(arg.Size(typ)) + } +} + +func assignSizes(types []sys.Type, args []*Arg) { + 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) { + 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) + } + + // 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) + continue + } + + arg, ok := argsMap[lenType.Buf] + if !ok { + panic(fmt.Sprintf("len field '%v' references non existent field '%v', argsMap: %+v, typesMap: %+v", + lenType.Name(), lenType.Buf, argsMap, typesMap)) + } + typ := typesMap[lenType.Buf] + + *lenArg = *generateSize(typ, arg, lenType) + } + } +} + +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) + } + } + 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) { switch c.Meta.CallName { case "mmap": diff --git a/prog/encoding.go b/prog/encoding.go index c9e9c52d4..01438f5b9 100644 --- a/prog/encoding.go +++ b/prog/encoding.go @@ -232,7 +232,7 @@ func parseArg(typ sys.Type, p *parser, vars map[string]*Arg) (*Arg, error) { return nil, fmt.Errorf("& arg is not a pointer: %#v", typ) } p.Parse('&') - page, off, err := parseAddr(p, true) + page, off, size, err := parseAddr(p, true) if err != nil { return nil, err } @@ -241,9 +241,9 @@ func parseArg(typ sys.Type, p *parser, vars map[string]*Arg) (*Arg, error) { if err != nil { return nil, err } - arg = pointerArg(page, off, inner) + arg = pointerArg(page, off, size, inner) case '(': - page, off, err := parseAddr(p, false) + page, off, _, err := parseAddr(p, false) if err != nil { return nil, err } @@ -368,22 +368,27 @@ func serializeAddr(a *Arg, base bool) string { } soff = fmt.Sprintf("%v0x%x", sign, off) } - return fmt.Sprintf("(0x%x%v)", page, soff) + ssize := "" + if size := a.AddrPagesNum; size != 0 { + size *= encodingPageSize + ssize = fmt.Sprintf("/0x%x", size) + } + return fmt.Sprintf("(0x%x%v%v)", page, soff, ssize) } -func parseAddr(p *parser, base bool) (uintptr, int, error) { +func parseAddr(p *parser, base bool) (uintptr, int, uintptr, error) { p.Parse('(') pstr := p.Ident() page, err := strconv.ParseUint(pstr, 0, 64) if err != nil { - return 0, 0, fmt.Errorf("failed to parse addr page: '%v'", pstr) + return 0, 0, 0, fmt.Errorf("failed to parse addr page: '%v'", pstr) } if page%encodingPageSize != 0 { - return 0, 0, fmt.Errorf("address base is not page size aligned: '%v'", pstr) + return 0, 0, 0, fmt.Errorf("address base is not page size aligned: '%v'", pstr) } if base { if page < encodingAddrBase { - return 0, 0, fmt.Errorf("address without base offset: '%v'", pstr) + return 0, 0, 0, fmt.Errorf("address without base offset: '%v'", pstr) } page -= encodingAddrBase } @@ -399,16 +404,26 @@ func parseAddr(p *parser, base bool) (uintptr, int, error) { ostr := p.Ident() off, err = strconv.ParseInt(ostr, 0, 64) if err != nil { - return 0, 0, fmt.Errorf("failed to parse addr offset: '%v'", ostr) + return 0, 0, 0, fmt.Errorf("failed to parse addr offset: '%v'", ostr) } if minus { page -= encodingPageSize off = -off } } + var size uint64 + if p.Char() == '/' { + p.Parse('/') + pstr := p.Ident() + size, err = strconv.ParseUint(pstr, 0, 64) + if err != nil { + return 0, 0, 0, fmt.Errorf("failed to parse addr size: '%v'", pstr) + } + } p.Parse(')') page /= encodingPageSize - return uintptr(page), int(off), nil + size /= encodingPageSize + return uintptr(page), int(off), uintptr(size), nil } type parser struct { diff --git a/prog/mutation.go b/prog/mutation.go index 068ebb849..577e24796 100644 --- a/prog/mutation.go +++ b/prog/mutation.go @@ -45,13 +45,13 @@ func (p *Prog) Mutate(rs rand.Source, ncalls int, ct *ChoiceTable) { } s := analyze(ct, p, c) for stop := false; !stop; stop = r.bin() { - args, bases, parents := mutationArgs(c) + args, bases := mutationArgs(c) if len(args) == 0 { retry = true return } idx := r.Intn(len(args)) - arg, base, parent := args[idx], bases[idx], parents[idx] + arg, base := args[idx], bases[idx] var baseSize uintptr if base != nil { if base.Kind != ArgPointer || base.Res == nil { @@ -59,12 +59,10 @@ func (p *Prog) Mutate(rs rand.Source, ncalls int, ct *ChoiceTable) { } baseSize = base.Res.Size(base.Res.Type) } - var size *Arg switch a := arg.Type.(type) { case sys.IntType, sys.FlagsType, sys.FileoffType, sys.ResourceType, sys.VmaType: - arg1, size1, calls1 := r.generateArg(s, arg.Type, arg.Dir, nil) + arg1, calls1 := r.generateArg(s, arg.Type, arg.Dir) p.replaceArg(arg, arg1, calls1) - size = size1 case sys.BufferType: switch a.Kind { case sys.BufferBlobRand, sys.BufferBlobRange: @@ -104,7 +102,6 @@ func (p *Prog) Mutate(rs rand.Source, ncalls int, ct *ChoiceTable) { default: panic("unknown buffer kind") } - size = constArg(uintptr(len(arg.Data))) case sys.FilenameType: filename := r.filename(s) arg.Data = []byte(filename) @@ -126,7 +123,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, nil) + arg1, calls1 := r.generateArg(s, a.Type, arg.Dir) arg.Inner = append(arg.Inner, arg1) for _, c1 := range calls1 { calls = append(calls, c1) @@ -147,10 +144,6 @@ func (p *Prog) Mutate(rs rand.Source, ncalls int, ct *ChoiceTable) { arg.Inner = arg.Inner[:count] } // TODO: swap elements of the array - size = constArg(count) - for _, elem := range arg.Inner { - size.ByteSize += elem.Size(a.Type) - } case sys.PtrType: // TODO: we don't know size for out args size := uintptr(1) @@ -175,10 +168,9 @@ func (p *Prog) Mutate(rs rand.Source, ncalls int, ct *ChoiceTable) { optType = a.Options[r.Intn(len(a.Options))] } p.removeArg(arg.Option) - opt, size1, calls := r.generateArg(s, optType, arg.Dir, nil) + opt, calls := r.generateArg(s, optType, arg.Dir) arg1 := unionArg(opt, optType) p.replaceArg(arg, arg1, calls) - size = size1 case sys.LenType: panic("bad arg returned by mutationArgs: LenType") case sys.ConstType, sys.StrConstType: @@ -187,42 +179,23 @@ func (p *Prog) Mutate(rs rand.Source, ncalls int, ct *ChoiceTable) { panic(fmt.Sprintf("bad arg returned by mutationArgs: %#v, type=%#v", *arg, arg.Type)) } - // Update associated size argument if there is one. - // TODO: update parent size. - if size != nil { - name := arg.Type.Name() - if name == "" && base != nil { - name = base.Type.Name() - } - for _, arg1 := range *parent { - if sz, ok := arg1.Type.(sys.LenType); ok && sz.Buf == name { - if arg1.Kind != ArgConst && arg1.Kind != ArgPageSize { - panic(fmt.Sprintf("size arg is not const: %#v", *arg1)) - } - arg1.Val = size.Val - if sz.ByteSize { - if size.Val != 0 && size.ByteSize == 0 { - panic(fmt.Sprintf("no byte size for %v in %v: size=%v", name, c.Meta.Name, size.Val)) - } - arg1.Val = size.ByteSize - } - arg1.AddrPage = size.AddrPage - arg1.AddrOffset = size.AddrOffset - } - } - } - // 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) - for _, c := range calls1 { - assignTypeAndDir(c) - sanitizeCall(c) + for _, c1 := range calls1 { + assignTypeAndDir(c1) + sanitizeCall(c1) } p.insertBefore(c, calls1) arg.AddrPage = arg1.AddrPage arg.AddrOffset = arg1.AddrOffset + arg.AddrPagesNum = arg1.AddrPagesNum } + + // Update all len fields. + assignSizesCall(c) + // Assign Arg.Type fields for newly created len args. + assignTypeAndDir(c) } }, 1, func() { @@ -284,7 +257,7 @@ func Minimize(p0 *Prog, callIndex0 int, pred func(*Prog, int) bool) (*Prog, int) mmap := &Call{ Meta: sys.CallMap["mmap"], Args: []*Arg{ - pointerArg(0, 0, nil), + 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), @@ -350,7 +323,7 @@ func (p *Prog) TrimAfter(idx int) { p.Calls = p.Calls[:idx+1] } -func mutationArgs(c *Call) (args, bases []*Arg, parents []*[]*Arg) { +func mutationArgs(c *Call) (args, bases []*Arg) { foreachArg(c, func(arg, base *Arg, parent *[]*Arg) { switch typ := arg.Type.(type) { case *sys.StructType: @@ -382,7 +355,6 @@ func mutationArgs(c *Call) (args, bases []*Arg, parents []*[]*Arg) { } args = append(args, arg) bases = append(bases, base) - parents = append(parents, parent) }) return } diff --git a/prog/mutation_test.go b/prog/mutation_test.go index 5d189e938..3ff30d1e9 100644 --- a/prog/mutation_test.go +++ b/prog/mutation_test.go @@ -50,10 +50,10 @@ func TestMutateTable(t *testing.T) { tests := [][2]string{ // Insert calls. { - "mmap(&(0x7f0000000000)=nil, (0x1000), 0x3, 0x32, 0xffffffffffffffff, 0x0)\n" + + "mmap(&(0x7f0000000000/0x1000)=nil, (0x1000), 0x3, 0x32, 0xffffffffffffffff, 0x0)\n" + "pipe2(&(0x7f0000000000)={0x0, 0x0}, 0x0)\n", - "mmap(&(0x7f0000000000)=nil, (0x1000), 0x3, 0x32, 0xffffffffffffffff, 0x0)\n" + + "mmap(&(0x7f0000000000/0x1000)=nil, (0x1000), 0x3, 0x32, 0xffffffffffffffff, 0x0)\n" + "sched_yield()\n" + "pipe2(&(0x7f0000000000)={0x0, 0x0}, 0x0)\n", }, @@ -119,7 +119,7 @@ func TestMutateTable(t *testing.T) { "r0 = open(&(0x7f0000001000)=\"2e2f66696c653000\", 0x22c0, 0x1)\n" + "readv(r0, &(0x7f0000000000)=[{&(0x7f0000001000)=nil, 0x1}, {&(0x7f0000002000)=nil, 0x2}], 0x2)\n", - "mmap(&(0x7f0000000000)=nil, (0x1000), 0x3, 0x32, 0xffffffffffffffff, 0x0)\n" + + "mmap(&(0x7f0000000000/0x1000)=nil, (0x1000), 0x3, 0x32, 0xffffffffffffffff, 0x0)\n" + "r0 = open(&(0x7f0000001000)=\"2e2f66696c653000\", 0x22c0, 0x1)\n" + "readv(r0, &(0x7f0000000000)=[{&(0x7f0000001000)=nil, 0x1}, {&(0x7f0000002000)=nil, 0x2}, {&(0x7f0000000000)=nil, 0x3}], 0x3)\n", }, @@ -158,7 +158,7 @@ func TestMinimize(t *testing.T) { }{ // Predicate always returns false, so must get the same program. { - "mmap(&(0x7f0000000000)=nil, (0x1000), 0x3, 0x32, 0xffffffffffffffff, 0x0)\n" + + "mmap(&(0x7f0000000000/0x1000)=nil, (0x1000), 0x3, 0x32, 0xffffffffffffffff, 0x0)\n" + "sched_yield()\n" + "pipe2(&(0x7f0000000000)={0x0, 0x0}, 0x0)\n", 2, @@ -171,14 +171,14 @@ func TestMinimize(t *testing.T) { } return false }, - "mmap(&(0x7f0000000000)=nil, (0x1000), 0x3, 0x32, 0xffffffffffffffff, 0x0)\n" + + "mmap(&(0x7f0000000000/0x1000)=nil, (0x1000), 0x3, 0x32, 0xffffffffffffffff, 0x0)\n" + "sched_yield()\n" + "pipe2(&(0x7f0000000000)={0x0, 0x0}, 0x0)\n", 2, }, // Remove a call. { - "mmap(&(0x7f0000000000)=nil, (0x1000), 0x3, 0x32, 0xffffffffffffffff, 0x0)\n" + + "mmap(&(0x7f0000000000/0x1000)=nil, (0x1000), 0x3, 0x32, 0xffffffffffffffff, 0x0)\n" + "sched_yield()\n" + "pipe2(&(0x7f0000000000)={0x0, 0x0}, 0x0)\n", 2, @@ -186,13 +186,13 @@ func TestMinimize(t *testing.T) { // Aim at removal of sched_yield. return len(p.Calls) == 2 && p.Calls[0].Meta.Name == "mmap" && p.Calls[1].Meta.Name == "pipe2" }, - "mmap(&(0x7f0000000000)=nil, (0x1000), 0x3, 0x32, 0xffffffffffffffff, 0x0)\n" + + "mmap(&(0x7f0000000000/0x1000)=nil, (0x1000), 0x3, 0x32, 0xffffffffffffffff, 0x0)\n" + "pipe2(&(0x7f0000000000)={0x0, 0x0}, 0x0)\n", 1, }, // Remove two dependent calls. { - "mmap(&(0x7f0000000000)=nil, (0x1000), 0x3, 0x32, 0xffffffffffffffff, 0x0)\n" + + "mmap(&(0x7f0000000000/0x1000)=nil, (0x1000), 0x3, 0x32, 0xffffffffffffffff, 0x0)\n" + "pipe2(&(0x7f0000000000)={0x0, 0x0}, 0x0)\n" + "sched_yield()\n", 2, @@ -211,7 +211,7 @@ func TestMinimize(t *testing.T) { }, // Remove a call and replace results. { - "mmap(&(0x7f0000000000)=nil, (0x1000), 0x3, 0x32, 0xffffffffffffffff, 0x0)\n" + + "mmap(&(0x7f0000000000/0x1000)=nil, (0x1000), 0x3, 0x32, 0xffffffffffffffff, 0x0)\n" + "pipe2(&(0x7f0000000000)={<r0=>0x0, 0x0}, 0x0)\n" + "write(r0, &(0x7f0000000000)=\"1155\", 0x2)\n" + "sched_yield()\n", @@ -219,14 +219,14 @@ func TestMinimize(t *testing.T) { func(p *Prog, callIndex int) bool { return p.String() == "mmap-write-sched_yield" }, - "mmap(&(0x7f0000000000)=nil, (0x1000), 0x3, 0x32, 0xffffffffffffffff, 0x0)\n" + + "mmap(&(0x7f0000000000/0x1000)=nil, (0x1000), 0x3, 0x32, 0xffffffffffffffff, 0x0)\n" + "write(0xffffffffffffffff, &(0x7f0000000000)=\"1155\", 0x2)\n" + "sched_yield()\n", 2, }, // Remove a call and replace results. { - "mmap(&(0x7f0000000000)=nil, (0x1000), 0x3, 0x32, 0xffffffffffffffff, 0x0)\n" + + "mmap(&(0x7f0000000000/0x1000)=nil, (0x1000), 0x3, 0x32, 0xffffffffffffffff, 0x0)\n" + "r0=open(&(0x7f0000000000)=\"1155\", 0x0, 0x0)\n" + "write(r0, &(0x7f0000000000)=\"1155\", 0x2)\n" + "sched_yield()\n", @@ -234,7 +234,7 @@ func TestMinimize(t *testing.T) { func(p *Prog, callIndex int) bool { return p.String() == "mmap-write-sched_yield" }, - "mmap(&(0x7f0000000000)=nil, (0x1000), 0x3, 0x32, 0xffffffffffffffff, 0x0)\n" + + "mmap(&(0x7f0000000000/0x1000)=nil, (0x1000), 0x3, 0x32, 0xffffffffffffffff, 0x0)\n" + "write(0xffffffffffffffff, &(0x7f0000000000)=\"1155\", 0x2)\n" + "sched_yield()\n", -1, @@ -242,15 +242,15 @@ func TestMinimize(t *testing.T) { // Glue several mmaps together. { "sched_yield()\n" + - "mmap(&(0x7f0000000000)=nil, (0x1000), 0x3, 0x32, 0xffffffffffffffff, 0x0)\n" + - "mmap(&(0x7f0000001000)=nil, (0x1000), 0x3, 0x32, 0xffffffffffffffff, 0x0)\n" + + "mmap(&(0x7f0000000000/0x1000)=nil, (0x1000), 0x3, 0x32, 0xffffffffffffffff, 0x0)\n" + + "mmap(&(0x7f0000001000/0x1000)=nil, (0x1000), 0x3, 0x32, 0xffffffffffffffff, 0x0)\n" + "getpid()\n" + - "mmap(&(0x7f0000005000)=nil, (0x2000), 0x3, 0x32, 0xffffffffffffffff, 0x0)\n", + "mmap(&(0x7f0000005000/0x5000)=nil, (0x2000), 0x3, 0x32, 0xffffffffffffffff, 0x0)\n", 3, func(p *Prog, callIndex int) bool { return p.String() == "mmap-sched_yield-getpid" }, - "mmap(&(0x7f0000000000)=nil, (0x7000), 0x3, 0x32, 0xffffffffffffffff, 0x0)\n" + + "mmap(&(0x7f0000000000/0x7000)=nil, (0x7000), 0x3, 0x32, 0xffffffffffffffff, 0x0)\n" + "sched_yield()\n" + "getpid()\n", 2, diff --git a/prog/prog.go b/prog/prog.go index d6c1bd325..e0a3ab235 100644 --- a/prog/prog.go +++ b/prog/prog.go @@ -20,20 +20,20 @@ type Call struct { } type Arg struct { - Call *Call - Type sys.Type - Kind ArgKind - Dir ArgDir - Val uintptr // value of ArgConst - ByteSize uintptr // size of an array in bytes for ArgConst - AddrPage uintptr // page index for ArgPointer address, page count for ArgPageSize - AddrOffset int // page offset for ArgPointer address - Data []byte // data of ArgData - Inner []*Arg // subargs of ArgGroup - Res *Arg // target of ArgResult, pointee for ArgPointer - Uses map[*Arg]bool // this arg is used by those ArgResult args - OpDiv uintptr // divide result for ArgResult (executed before OpAdd) - OpAdd uintptr // add to result for ArgResult + 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 + AddrPagesNum uintptr // number of available pages for ArgPointer + Data []byte // data of ArgData + Inner []*Arg // subargs of ArgGroup + Res *Arg // target of ArgResult, pointee for ArgPointer + Uses map[*Arg]bool // this arg is used by those ArgResult args + OpDiv uintptr // divide result for ArgResult (executed before OpAdd) + OpAdd uintptr // add to result for ArgResult // ArgUnion/UnionType Option *Arg @@ -61,6 +61,24 @@ const ( 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: + 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 (a *Arg) Size(typ sys.Type) uintptr { switch typ1 := typ.(type) { case sys.IntType, sys.LenType, sys.FlagsType, sys.ConstType, sys.StrConstType, @@ -109,8 +127,8 @@ func dataArg(data []byte) *Arg { return &Arg{Kind: ArgData, Data: append([]byte{}, data...)} } -func pointerArg(page uintptr, off int, obj *Arg) *Arg { - return &Arg{Kind: ArgPointer, AddrPage: page, AddrOffset: off, Res: obj} +func pointerArg(page uintptr, off int, npages uintptr, obj *Arg) *Arg { + return &Arg{Kind: ArgPointer, AddrPage: page, AddrOffset: off, AddrPagesNum: npages, Res: obj} } func pageSizeArg(npages uintptr, off int) *Arg { diff --git a/prog/rand.go b/prog/rand.go index b96ecbe9a..f5fecc73b 100644 --- a/prog/rand.go +++ b/prog/rand.go @@ -405,8 +405,11 @@ func (r *randGen) timespec(s *state, usec bool) (arg *Arg, calls []*Call) { func (r *randGen) addr1(s *state, 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), nil + return r.randPageAddr(s, npages, data, false), nil } for i := uintptr(0); i < maxPages-npages; i++ { free := true @@ -422,7 +425,7 @@ func (r *randGen) addr1(s *state, size uintptr, data *Arg) (*Arg, []*Call) { c := &Call{ Meta: sys.CallMap["mmap"], Args: []*Arg{ - pointerArg(i, 0, nil), + 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), @@ -430,9 +433,9 @@ func (r *randGen) addr1(s *state, size uintptr, data *Arg) (*Arg, []*Call) { constArg(0), }, } - return pointerArg(i, 0, data), []*Call{c} + return pointerArg(i, 0, 0, data), []*Call{c} } - return r.randPageAddr(s, npages, data), nil + return r.randPageAddr(s, npages, data, false), nil } func (r *randGen) addr(s *state, size uintptr, data *Arg) (*Arg, []*Call) { @@ -454,7 +457,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) *Arg { +func (r *randGen) randPageAddr(s *state, npages uintptr, data *Arg, vma bool) *Arg { var starts []uintptr for i := uintptr(0); i < maxPages-npages; i++ { busy := true @@ -471,11 +474,16 @@ func (r *randGen) randPageAddr(s *state, npages uintptr, data *Arg) *Arg { } starts = append(starts, i) } + var page uintptr if len(starts) != 0 { - return pointerArg(starts[r.rand(len(starts))], 0, data) + page = starts[r.rand(len(starts))] } else { - return pointerArg(r.rand(int(maxPages-npages)), 0, data) + page = r.rand(int(maxPages-npages)) + } + if !vma { + npages = 0 } + return pointerArg(page, 0, npages, data) } func (r *randGen) createResource(s *state, res sys.ResourceType) (arg *Arg, calls []*Call) { @@ -604,51 +612,23 @@ func (r *randGen) generateParticularCall(s *state, meta *sys.Call) (calls []*Cal func (r *randGen) generateArgs(s *state, types []sys.Type, dir ArgDir) ([]*Arg, []*Call) { var calls []*Call args := make([]*Arg, len(types)) - sizes := make(map[string]*Arg) - // Pass 1: generate all args except size arguments. + + // Generate all args. Size args have the default value 0 for now. for i, typ := range types { - if _, ok := typ.(sys.LenType); ok { - continue + arg, calls1 := r.generateArg(s, typ, dir) + if arg == nil { + panic(fmt.Sprintf("generated arg is nil for type '%v', types: %+v", typ.Name(), types)) } - arg, size, calls1 := r.generateArg(s, typ, dir, sizes) args[i] = arg calls = append(calls, calls1...) - if size != nil { - sizes[typ.Name()] = size - } } - // Pass 2: calculate size of the whole struct. - // Now we know sizes of all non-size arguments and size arguments are const-size. - var parentSize uintptr - for i, typ := range types { - parentSize += args[i].Size(typ) - } - if sizes["parent"] != nil { - panic("parent is reserved len name") - } - sizes["parent"] = constArg(parentSize) + assignSizes(types, args) - // Pass 3: fill in size arguments. - for i, typ := range types { - if a, ok := typ.(sys.LenType); ok { - size := sizes[a.Buf] - if size == nil { - panic(fmt.Sprintf("no size for %v[%v] (%+v), types: %+v", a.Name(), a.Buf, sizes, types)) - } - if a.ByteSize { - if size.Val != 0 && size.ByteSize == 0 { - panic(fmt.Sprintf("no byte size for %v: size=%v", a.Name(), size.Val)) - } - size = constArg(size.ByteSize) - } - args[i] = size - } - } return args, calls } -func (r *randGen) generateArg(s *state, typ sys.Type, dir ArgDir, sizes map[string]*Arg) (arg, size *Arg, calls []*Call) { +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 @@ -656,7 +636,7 @@ func (r *randGen) generateArg(s *state, typ sys.Type, dir ArgDir, sizes map[stri // 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 + return constArg(0), nil } } @@ -664,7 +644,7 @@ func (r *randGen) generateArg(s *state, typ sys.Type, dir ArgDir, sizes map[stri if _, ok := typ.(sys.BufferType); ok { panic("impossible") // parent PtrType must be Optional instead } - return constArg(typ.Default()), constArg(0), nil + return constArg(typ.Default()), nil } switch a := typ.(type) { @@ -694,7 +674,7 @@ func (r *randGen) generateArg(s *state, typ sys.Type, dir ArgDir, sizes map[stri arg, calls = r.createResource(s, a) }, ) - return arg, nil, calls + return arg, calls case sys.FileoffType: // TODO: can do better var arg *Arg @@ -703,7 +683,7 @@ func (r *randGen) generateArg(s *state, typ sys.Type, dir ArgDir, sizes map[stri 10, func() { arg = constArg(r.rand(100)) }, 1, func() { arg = constArg(r.randInt()) }, ) - return arg, nil, nil + return arg, nil case sys.BufferType: switch a.Kind { case sys.BufferBlobRand, sys.BufferBlobRange: @@ -717,19 +697,21 @@ func (r *randGen) generateArg(s *state, typ sys.Type, dir ArgDir, sizes map[stri data[i] = byte(r.Intn(256)) } } - return dataArg(data), constArg(sz), nil + return dataArg(data), nil case sys.BufferString: data := r.randString(s) - return dataArg(data), constArg(uintptr(len(data))), nil + return dataArg(data), nil case sys.BufferFilesystem: data := r.filesystem(s) - return dataArg(data), constArg(uintptr(len(data))), nil + return dataArg(data), nil case sys.BufferSockaddr: data := r.sockaddr(s) if dir == DirOut { - return nil, constArg(uintptr(len(data))), nil + for i := range data { + data[i] = 0 + } } - return dataArg(data), constArg(uintptr(len(data))), nil + return dataArg(data), nil case sys.BufferAlgType: data := r.algType(s) if dir == DirOut { @@ -737,7 +719,7 @@ func (r *randGen) generateArg(s *state, typ sys.Type, dir ArgDir, sizes map[stri data[i] = 0 } } - return dataArg(data), constArg(uintptr(len(data))), nil + return dataArg(data), nil case sys.BufferAlgName: data := r.algName(s) if dir == DirOut { @@ -745,20 +727,20 @@ func (r *randGen) generateArg(s *state, typ sys.Type, dir ArgDir, sizes map[stri data[i] = 0 } } - return dataArg(data), constArg(uintptr(len(data))), nil + return dataArg(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 + arg := r.randPageAddr(s, npages, nil, true) + return arg, nil case sys.FlagsType: - return constArg(r.flags(a.Vals)), nil, nil + return constArg(r.flags(a.Vals)), nil case sys.ConstType: - return constArg(a.Val), nil, nil + return constArg(a.Val), nil case sys.StrConstType: - return dataArg([]byte(a.Val)), constArg(uintptr(len(a.Val))), nil + return dataArg([]byte(a.Val)), nil case sys.IntType: v := r.randInt() switch a.Kind { @@ -771,10 +753,10 @@ func (r *randGen) generateArg(s *state, typ sys.Type, dir ArgDir, sizes map[stri case sys.IntRange: v = r.randRangeInt(a.RangeBegin, a.RangeEnd) } - return constArg(v), nil, nil + return constArg(v), nil case sys.FilenameType: filename := r.filename(s) - return dataArg([]byte(filename)), nil, nil + return dataArg([]byte(filename)), nil case sys.ArrayType: count := uintptr(0) switch a.Kind { @@ -783,16 +765,14 @@ func (r *randGen) generateArg(s *state, typ sys.Type, dir ArgDir, sizes map[stri case sys.ArrayRangeLen: count = r.randRange(int(a.RangeBegin), int(a.RangeEnd)) } - sz := constArg(count) var inner []*Arg var calls []*Call for i := uintptr(0); i < count; i++ { - arg1, _, calls1 := r.generateArg(s, a.Type, dir, nil) + arg1, calls1 := r.generateArg(s, a.Type, dir) inner = append(inner, arg1) calls = append(calls, calls1...) - sz.ByteSize += arg1.Size(a.Type) } - return groupArg(inner), sz, calls + return groupArg(inner), calls case *sys.StructType: if ctor := isSpecialStruct(a); ctor != nil && dir != DirOut { arg, calls = ctor(r, s) @@ -800,39 +780,33 @@ func (r *randGen) generateArg(s *state, typ sys.Type, dir ArgDir, sizes map[stri } args, calls := r.generateArgs(s, a.Fields, dir) group := groupArg(args) - return group, constArg(group.Size(a)), calls + return group, 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 + opt, calls := r.generateArg(s, optType, dir) + return unionArg(opt, optType), calls case sys.PtrType: - inner, size, calls := r.generateArg(s, a.Type, ArgDir(a.Dir), sizes) + 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, size.Val, nil) + arg, calls1 := r.addr(s, inner.Size(a.Type), nil) calls = append(calls, calls1...) - return arg, size, calls - } - if size == nil { - size = constArg(inner.Size(a.Type)) + 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, inner) - return arg, size, calls + 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, size, calls + return arg, 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 + // Return placeholder value of 0 while generating len args. + return constArg(0), nil default: panic("unknown argument type") } diff --git a/prog/size_test.go b/prog/size_test.go new file mode 100644 index 000000000..41068e713 --- /dev/null +++ b/prog/size_test.go @@ -0,0 +1,123 @@ +// Copyright 2016 syzkaller project authors. All rights reserved. +// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. + +package prog + +import ( + "bytes" + "strings" + "testing" +) + +func TestAssignSizeRandom(t *testing.T) { + rs, iters := initTest(t) + for i := 0; i < iters; i++ { + p := Generate(rs, 10, nil) + 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) + } + for try := 0; try <= 10; try++ { + p.Mutate(rs, 10, nil) + 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) + } + } + } +} + +func TestAssignSize(t *testing.T) { + tests := []struct { + unsizedProg string + sizedProg string + }{ + { + "syz_test$length0(&(0x7f0000000000)={0xff, 0x0})", + "syz_test$length0(&(0x7f0000000000)={0xff, 0x2})", + }, + { + "syz_test$length1(&(0x7f0000001000)={0xff, 0x0})", + "syz_test$length1(&(0x7f0000001000)={0xff, 0x4})", + }, + { + "syz_test$length2(&(0x7f0000001000)={0xff, 0x0})", + "syz_test$length2(&(0x7f0000001000)={0xff, 0x8})", + }, + { + "syz_test$length3(&(0x7f0000005000)={0xff, 0x0, 0x0})", + "syz_test$length3(&(0x7f0000005000)={0xff, 0x4, 0x2})", + }, + { + "syz_test$length4(&(0x7f0000003000)={0x0, 0x0})", + "syz_test$length4(&(0x7f0000003000)={0x2, 0x2})", + }, + { + "syz_test$length5(&(0x7f0000002000)={0xff, 0x0})", + "syz_test$length5(&(0x7f0000002000)={0xff, 0x4})", + }, + { + "syz_test$length6(&(0x7f0000002000)={[0xff, 0xff, 0xff, 0xff], 0x0})", + "syz_test$length6(&(0x7f0000002000)={[0xff, 0xff, 0xff, 0xff], 0x4})", + }, + { + "syz_test$length7(&(0x7f0000003000)={[0xff, 0xff, 0xff, 0xff], 0x0})", + "syz_test$length7(&(0x7f0000003000)={[0xff, 0xff, 0xff, 0xff], 0x8})", + }, + { + "syz_test$length8(&(0x7f000001f000)={0x00, {0xff, 0x0, 0x00, [0xff, 0xff, 0xff]}, [{0xff, 0x0, 0x00, [0xff, 0xff, 0xff]}], 0x00, 0x0, [0xff, 0xff]})", + "syz_test$length8(&(0x7f000001f000)={0x32, {0xff, 0x1, 0x10, [0xff, 0xff, 0xff]}, [{0xff, 0x1, 0x10, [0xff, 0xff, 0xff]}], 0x10, 0x1, [0xff, 0xff]})", + }, + { + "syz_test$length9(&(0x7f000001f000)={&(0x7f0000000000/0x5000)=nil, (0x0000)})", + "syz_test$length9(&(0x7f000001f000)={&(0x7f0000000000/0x5000)=nil, (0x5000)})", + }, + { + "syz_test$length10(&(0x7f0000000000/0x5000)=nil, (0x0000))", + "syz_test$length10(&(0x7f0000000000/0x5000)=nil, (0x5000))", + }, + { + "syz_test$length11(&(0x7f0000000000)={0xff, 0xff, [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]}, 0x00)", + "syz_test$length11(&(0x7f0000000000)={0xff, 0xff, [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]}, 0x30)", + }, + { + "syz_test$length12(&(0x7f0000000000)={0xff, 0xff, [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]}, 0x00)", + "syz_test$length12(&(0x7f0000000000)={0xff, 0xff, [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]}, 0x30)", + }, + { + "syz_test$length13(&(0x7f0000000000)={0xff, 0xff, [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]}, &(0x7f0000001000)=0x00)", + "syz_test$length13(&(0x7f0000000000)={0xff, 0xff, [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]}, &(0x7f0000001000)=0x30)", + }, + { + "syz_test$length14(&(0x7f0000000000)={0xff, 0xff, [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]}, &(0x7f0000001000)=0x00)", + "syz_test$length14(&(0x7f0000000000)={0xff, 0xff, [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]}, &(0x7f0000001000)=0x30)", + }, + { + "syz_test$length15(0xff, 0x0)", + "syz_test$length15(0xff, 0x2)", + }, + } + + for i, test := range tests { + p, err := Deserialize([]byte(test.unsizedProg)) + if err != nil { + t.Fatalf("failed to deserialize prog %v: %v", i, err) + } + for _, call := range p.Calls { + assignSizesCall(call) + assignTypeAndDir(call) + } + p1 := strings.TrimSpace(string(p.Serialize())) + if p1 != test.sizedProg { + t.Fatalf("failed to assign sizes in prog %v\ngot %v\nwant %v", i, p1, test.sizedProg) + } + } +} diff --git a/prog/validation.go b/prog/validation.go index efd178a41..93bc94170 100644 --- a/prog/validation.go +++ b/prog/validation.go @@ -120,12 +120,18 @@ func (c *Call) validate(ctx *validCtx) error { 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: if arg.Res != nil { if err := checkArg(arg.Res, typ1.Type); err != nil { return err } } + if arg.AddrPagesNum != 0 { + return fmt.Errorf("syscall %v: pointer arg '%v' has nonzero size", c.Meta.Name, typ.Name()) + } default: return fmt.Errorf("syscall %v: pointer arg '%v' has bad meta type %+v", c.Meta.Name, typ.Name(), typ) } diff --git a/sys/README.md b/sys/README.md index 1355ad4f8..18b3c76ab 100644 --- a/sys/README.md +++ b/sys/README.md @@ -21,8 +21,9 @@ Pseudo-formal grammar of syscall description: arg = argname type argname = identifier type = typename [ "[" type-options "]" ] - typename = "fileoff" | "buffer" | "vma" , "len" | "flags" | - "filename" | "ptr" | "array" | "intN" | "intptr" + typename = "const" | "intN" | "intptr" | "flags" | "array" | "ptr" | + "buffer" | "string" | "strconst" | "filename" | + "fileoff" | "len" | "bytesize" | "vma" type-options = [type-opt ["," type-opt]] ``` common type-options include: @@ -31,23 +32,29 @@ common type-options include: ``` rest of the type-options are type-specific: ``` - "fileoff": offset within a file, type-options: - argname of the file + "const": integer constant, type-options: + value, underlying type (one if "intN", "intptr") + "intN"/"intptr": an integer without a particular meaning, type-options: + optional range of values (e.g. "5:10", or "-100:200") + "flags": a set of flags, type-options: + reference to flags description (see below) + "array": a variable/fixed-length array, type-options: + type of elements, optional size (fixed "5", or ranged "5:10", boundaries inclusive) + "ptr": a pointer to an object, type-options: + type of the object; direction (in/out/inout) "buffer": a pointer to a memory buffer (like read/write buffer argument), type-options: direction (in/out/inout) "string": a pointer to a memory buffer, similar to buffer[in] - "vma": a pointer to a set of pages (used as input for mmap/munmap/mremap/madvise) - "len": length of buffer/vma/arrayptr (for array it is number of elements), type-options: - argname of the object - "flags": a set of flags, type-options: - reference to flags description + "strconst": a pointer to a constant string, type-options: + the underlying string (for example "/dev/dsp") "filename": a file/link/dir name - "ptr": a pointer to an object, type-options: - type of the object; direction (in/out/inout) - "array": a variable/fixed-length array, type-options: - type of elements, optional size (fixed "5", or ranged "5:10", boundaries inclusive) - "intN"/"intptr": an integer without a particular meaning, type-options: - range of values (e.g. "5:10", or "-100:200", optional) + "fileoff": offset within a file, type-options: + argname of the file + "len": length of another field (for array it is number of elements), type-options: + argname of the object + "bytesize": similar to "len", but always denotes the size in bytes, type-options: + argname of the object + "vma": a pointer to a set of pages (used as input for mmap/munmap/mremap/madvise) ``` flags/len/flags also have trailing underlying type type-option when used in structs/unions/pointers. @@ -56,6 +63,8 @@ Flags are described as: flagname = const ["," const]* ``` +### Structs + Structs are described as: ``` structname "{" "\n" @@ -65,6 +74,8 @@ Structs are described as: Structs can have trailing attributes "packed" and "align_N", they are specified in square brackets after the struct. +### Unions + Unions are described as: ``` unionname "[" "\n" @@ -73,8 +84,9 @@ Unions are described as: ``` Unions can have a trailing "varlen" attribute (specified in square brackets after the union), which means that union length is not maximum of all option lengths, -but rather length of a particular chosen option (such unions can't be part of a struct, -because their size is not statically known). +but rather length of a particular chosen option. + +### Resources Custom resources are described as: ``` @@ -92,6 +104,8 @@ accept(fd sock, ...) sock listen(fd sock, backlog int32) ``` +### Misc + Description files also contain `include` directives that refer to Linux kernel header files and `define` directives that define symbolic constant values. See the following section for details. diff --git a/sys/decl.go b/sys/decl.go index 275bbb380..ba38399ba 100644 --- a/sys/decl.go +++ b/sys/decl.go @@ -25,6 +25,7 @@ type Type interface { Default() uintptr Size() uintptr Align() uintptr + InnerType() Type // returns inner type for PtrType } func IsPad(t Type) bool { @@ -83,6 +84,10 @@ func (t ResourceType) Align() uintptr { return t.Desc.Type.Align() } +func (t ResourceType) InnerType() Type { + return t +} + type FileoffType struct { TypeCommon TypeSize uintptr @@ -97,6 +102,10 @@ func (t FileoffType) Align() uintptr { return t.Size() } +func (t FileoffType) InnerType() Type { + return t +} + type BufferKind int const ( @@ -136,6 +145,10 @@ func (t BufferType) Align() uintptr { return 1 } +func (t BufferType) InnerType() Type { + return t +} + type VmaType struct { TypeCommon } @@ -148,6 +161,10 @@ func (t VmaType) Align() uintptr { return t.Size() } +func (t VmaType) InnerType() Type { + return t +} + type LenType struct { TypeCommon TypeSize uintptr @@ -163,6 +180,10 @@ func (t LenType) Align() uintptr { return t.Size() } +func (t LenType) InnerType() Type { + return t +} + type FlagsType struct { TypeCommon TypeSize uintptr @@ -177,6 +198,10 @@ func (t FlagsType) Align() uintptr { return t.Size() } +func (t FlagsType) InnerType() Type { + return t +} + type ConstType struct { TypeCommon TypeSize uintptr @@ -192,6 +217,10 @@ func (t ConstType) Align() uintptr { return t.Size() } +func (t ConstType) InnerType() Type { + return t +} + type StrConstType struct { TypeCommon TypeSize uintptr @@ -206,6 +235,10 @@ func (t StrConstType) Align() uintptr { return t.Size() } +func (t StrConstType) InnerType() Type { + return t +} + type IntKind int const ( @@ -232,6 +265,10 @@ func (t IntType) Align() uintptr { return t.Size() } +func (t IntType) InnerType() Type { + return t +} + type FilenameType struct { TypeCommon } @@ -244,6 +281,10 @@ func (t FilenameType) Align() uintptr { return 1 } +func (t FilenameType) InnerType() Type { + return t +} + type ArrayKind int const ( @@ -270,6 +311,10 @@ func (t ArrayType) Align() uintptr { return t.Type.Align() } +func (t ArrayType) InnerType() Type { + return t +} + type PtrType struct { TypeCommon Type Type @@ -284,6 +329,10 @@ func (t PtrType) Align() uintptr { return t.Size() } +func (t PtrType) InnerType() Type { + return t.Type.InnerType() +} + type StructType struct { TypeCommon Fields []Type @@ -316,6 +365,10 @@ func (t *StructType) Align() uintptr { return align } +func (t *StructType) InnerType() Type { + return t +} + type UnionType struct { TypeCommon Options []Type @@ -345,6 +398,10 @@ func (t *UnionType) Align() uintptr { return align } +func (t *UnionType) InnerType() Type { + return t +} + type Dir int const ( diff --git a/sys/fuse.txt b/sys/fuse.txt index 64f9accc9..c78b5b745 100644 --- a/sys/fuse.txt +++ b/sys/fuse.txt @@ -91,13 +91,13 @@ fuse_notify_poll_wakeup_out { } fuse_notify_inval_inode_out { - len len[parent, int32] + len1 len[parent, int32] err int32 unique const[0, int64] ino int64 off int64 - len int16 + len2 int16 } fuse_notify_inval_entry_out { @@ -105,7 +105,7 @@ fuse_notify_inval_entry_out { err int32 unique const[0, int64] - parent int64 + par int64 namelen int32 } @@ -114,7 +114,7 @@ fuse_notify_delete_out { err int32 unique const[0, int64] - parent int64 + par int64 child int64 namelen int32 } @@ -132,9 +132,9 @@ fuse_notify_store_out { fuse_notify_retrieve_out { len len[parent, int32] err int32 - unique const[0, int64] + unique1 const[0, int64] - unique int64 + unique2 int64 nodeid int64 off int64 size int32 diff --git a/sys/kdbus.txt b/sys/kdbus.txt index 4c3caa427..5d40b8625 100644 --- a/sys/kdbus.txt +++ b/sys/kdbus.txt @@ -51,10 +51,10 @@ kdbus_cmd_ep_update { kdbus_cmd_hello { size len[parent, int64] flags flags[kdbus_hello_flags, int64] - rflags const[0, int64] + rflags1 const[0, int64] sflags flags[kdbus_attach_flags, int64] - rflags flags[kdbus_attach_flags, int64] + rflags2 flags[kdbus_attach_flags, int64] bflags int64 id int64 poolsz int64 @@ -248,32 +248,32 @@ kdbus_pids_parameter { size len[parent, int64] type const[KDBUS_ITEM_PIDS, int64] pid pid - pad const[0, int32] + pad1 const[0, int32] tid pid - pad const[0, int32] + pad2 const[0, int32] ppid pid - pad const[0, int32] + pad3 const[0, int32] } kdbus_creds_parameter { size len[parent, int64] type const[KDBUS_ITEM_CREDS, int64] uid uid - pad const[0, int32] + pad1 const[0, int32] euid uid - pad const[0, int32] + pad2 const[0, int32] suid uid - pad const[0, int32] + pad3 const[0, int32] fsuid uid - pad const[0, int32] + pad4 const[0, int32] gid uid - pad const[0, int32] + pad5 const[0, int32] egid uid - pad const[0, int32] + pad6 const[0, int32] sgid uid - pad const[0, int32] + pad7 const[0, int32] fsgid uid - pad const[0, int32] + pad8 const[0, int32] } kdbus_seclabel_parameter { @@ -284,17 +284,17 @@ kdbus_seclabel_parameter { } kdbus_payload_vec { - size len[parent, int64] + size1 len[parent, int64] type const[KDBUS_ITEM_PAYLOAD_VEC, int64] - size len[data, int64] + size2 len[data, int64] data buffer[in] } kdbus_payload_memfd { - size len[parent, int64] + size1 len[parent, int64] type const[KDBUS_ITEM_PAYLOAD_MEMFD, int64] start int64 - size int64 + size2 int64 fd fd } diff --git a/sys/kvm.txt b/sys/kvm.txt index eed748626..b80f4ce72 100644 --- a/sys/kvm.txt +++ b/sys/kvm.txt @@ -315,7 +315,7 @@ kvm_vcpu_events { exinjec int8 exnr int8 exhec int8 - pad const[0, int8] + pad1 const[0, int8] exec int32 ininjec int8 @@ -326,7 +326,7 @@ kvm_vcpu_events { nmiinj int8 nmipend int8 nmimask int8 - pad const[0, int8] + pad2 const[0, int8] sipi int32 flags int32 diff --git a/sys/sndcontrol.txt b/sys/sndcontrol.txt index a209c1796..20d5cc320 100644 --- a/sys/sndcontrol.txt +++ b/sys/sndcontrol.txt @@ -63,9 +63,9 @@ snd_ctl_elem_info { name array[int8, 64] nameptr string namelen len[nameptr, int32] - pad array[const[0, int8], 44] + pad1 array[const[0, int8], 44] d array[int16, 4] - pad array[const[0, int8], 56] + pad2 array[const[0, int8], 56] } snd_ctl_elem_value { diff --git a/sys/sndseq.txt b/sys/sndseq.txt index cffc3ae93..62a844ed7 100644 --- a/sys/sndseq.txt +++ b/sys/sndseq.txt @@ -65,8 +65,8 @@ snd_seq_running_info { client int8 bigend int8 cpumode int8 - pad const[0, int8] - pad array[const[0, int8], 12] + pad1 const[0, int8] + pad2 array[const[0, int8], 12] } snd_seq_client_info { @@ -105,8 +105,8 @@ snd_seq_port_subscribe { voices int32 flags flags[snd_seq_sub_flags, int32] queue int8 - pad array[const[0, int8], 3] - pad array[const[0, int8], 64] + pad1 array[const[0, int8], 3] + pad2 array[const[0, int8], 64] } snd_seq_queue_info { diff --git a/sys/sndtimer.txt b/sys/sndtimer.txt index bddfa1a22..67e0a13a8 100644 --- a/sys/sndtimer.txt +++ b/sys/sndtimer.txt @@ -32,12 +32,12 @@ snd_timer_ginfo { # TODO: the following two fields should be a fixed-size embeded string. id array[int8, 64] name array[int8, 80] - pad const[0, intptr] + pad1 const[0, intptr] res intptr resmin intptr resmax intptr clients int32 - pad array[const[0, int8], 32] + pad2 array[const[0, int8], 32] } snd_timer_gparams { @@ -64,7 +64,7 @@ snd_timer_params { flags flags[snd_timer_flags, int32] ticks int32 qsize int32 - pad const[0, int32] + pad1 const[0, int32] filter flags[snd_timer_filter, int32] - pad array[const[0, int8], 60] + pad2 array[const[0, int8], 60] } diff --git a/sys/sys.txt b/sys/sys.txt index 89e21ca90..be0d10860 100644 --- a/sys/sys.txt +++ b/sys/sys.txt @@ -224,7 +224,7 @@ prctl$setname(option const[PR_SET_NAME], name string) prctl$getname(option const[PR_GET_NAME], name buffer[out]) prctl$setptracer(option const[PR_SET_PTRACER], pid pid) prctl$seccomp(option const[PR_SET_SECCOMP], mode flags[prctl_seccomp_mode], prog ptr[in, sock_fprog]) -prctl$setmm(option const[PR_SET_MM], option flags[prctl_mm_option], val vma) +prctl$setmm(option1 const[PR_SET_MM], option2 flags[prctl_mm_option], val vma) arch_prctl(code flags[arch_prctl_code], addr buffer[in]) seccomp(op flags[seccomp_op], flags flags[seccomp_flags], prog ptr[in, sock_fprog]) @@ -315,7 +315,7 @@ inotify_init1(flags flags[inotify_flags]) fd_inotify inotify_add_watch(fd fd_inotify, file filename, mask flags[inotify_mask]) inotifydesc inotify_rm_watch(fd fd_inotify, wd inotifydesc) fanotify_init(flags flags[fanotify_flags], events flags[fanotify_events]) fd_fanotify -fanotify_mark(fd fd_fanotify, flags flags[fanotify_mark], mask flags[fanotify_mask], fd fd_dir, path filename) +fanotify_mark(fd fd_fanotify, flags flags[fanotify_mark], mask flags[fanotify_mask], fddir fd_dir, path filename) link(old filename, new filename) linkat(oldfd fd_dir, old filename, newfd fd_dir, new filename, flags flags[linkat_flags]) @@ -548,8 +548,8 @@ stat { mnsec int32 ctime int32 cnsec int32 - pad int32 - pad int32 + pad1 int32 + pad2 int32 } pollfd { @@ -705,7 +705,6 @@ shmid_ds { seq int16 segsz intptr atime intptr - atime intptr dtime intptr ctime intptr cpid pid @@ -1038,12 +1037,12 @@ fiemap_extent { logical int64 phys int64 len int64 - pad const[0, int64] - pad const[0, int64] + pad1 const[0, int64] + pad2 const[0, int64] flags flags[fiemap_extent_flags, int32] - pad const[0, int32] - pad const[0, int32] - pad const[0, int32] + pad3 const[0, int32] + pad4 const[0, int32] + pad5 const[0, int32] } uffdio_api { diff --git a/sys/test.txt b/sys/test.txt index 6420ed54c..bf66d83aa 100644 --- a/sys/test.txt +++ b/sys/test.txt @@ -76,3 +76,93 @@ syz_array_blob { f1 array[int8, 16] f2 int16 } + +# Length. + +syz_test$length0(a0 ptr[in, syz_length_int_struct]) +syz_test$length1(a0 ptr[in, syz_length_const_struct]) +syz_test$length2(a0 ptr[in, syz_length_flags_struct]) +syz_test$length3(a0 ptr[in, syz_length_len_struct]) +syz_test$length4(a0 ptr[in, syz_length_len2_struct]) +syz_test$length5(a0 ptr[in, syz_length_parent_struct]) +syz_test$length6(a0 ptr[in, syz_length_array_struct]) +syz_test$length7(a0 ptr[in, syz_length_array2_struct]) +syz_test$length8(a0 ptr[in, syz_length_complex_struct]) +syz_test$length9(a0 ptr[in, syz_length_vma_struct]) + +syz_test$length10(a0 vma, a1 len[a0]) +syz_test$length11(a0 ptr[in, syz_length_large_struct], a1 len[a0]) +syz_test$length12(a0 ptr[in, syz_length_large_struct, opt], a1 len[a0]) +syz_test$length13(a0 ptr[inout, syz_length_large_struct], a1 ptr[inout, len[a0, int64]]) +syz_test$length14(a0 ptr[inout, syz_length_large_struct], a1 ptr[inout, len[a0, int64], opt]) +syz_test$length15(a0 int16, a1 len[a0]) + +syz_length_flags = 0, 1 + +syz_length_int_struct { + f0 int16 + f1 len[f0, int16] +} + +syz_length_const_struct { + f0 const[0, int32] + f1 len[f0, int32] +} + +syz_length_flags_struct { + f0 flags[syz_length_flags, int64] + f1 len[f0, int64] +} + +syz_length_len_struct { + f0 int32 + f1 len[f0, int16] + f2 len[f1, int16] +} + +syz_length_len2_struct { + f0 len[f1, int16] + f1 len[f0, int16] +} + +syz_length_parent_struct { + f0 int16 + f1 len[parent, int16] +} + +syz_length_array_struct { + f0 array[int16, 4] + f1 len[f0, int16] +} + +syz_length_array2_struct { + f0 array[int16, 4] + f1 bytesize[f0, int16] +} + +syz_length_complex_inner_struct { + f0 int8 + f1 len[f0, int8] + f2 len[parent, int16] + f3 array[int32, 3] +} + +syz_length_complex_struct { + f0 len[parent, int64] + f1 syz_length_complex_inner_struct + f2 array[syz_length_complex_inner_struct, 1] + f3 len[f1, int32] + f4 len[f2, int16] + f5 array[int16] +} + +syz_length_vma_struct { + f0 vma + f1 len[f0, int64] +} + +syz_length_large_struct { + f0 int64 + f1 int64 + f2 array[int32, 8] +} diff --git a/sysgen/sysgen.go b/sysgen/sysgen.go index c09a7cd7e..28507db88 100644 --- a/sysgen/sysgen.go +++ b/sysgen/sysgen.go @@ -158,7 +158,7 @@ func generate(arch string, desc *Description, consts map[string]uint64, out io.W fmt.Fprintf(out, "func() { Calls = append(Calls, &Call{Name: \"%v\", CallName: \"%v\"", s.Name, s.CallName) if len(s.Ret) != 0 { fmt.Fprintf(out, ", Ret: ") - generateArg("ret", s.Ret[0], s.Ret[1:], desc, consts, true, false, out) + generateArg("", "ret", s.Ret[0], s.Ret[1:], desc, consts, true, false, out) } fmt.Fprintf(out, ", Args: []Type{") for i, a := range s.Args { @@ -166,7 +166,7 @@ func generate(arch string, desc *Description, consts map[string]uint64, out io.W fmt.Fprintf(out, ", ") } logf(5, " generate description for arg %v", i) - generateArg(a[0], a[1], a[2:], desc, consts, true, false, out) + generateArg("", a[0], a[1], a[2:], desc, consts, true, false, out) } if skipCurrentSyscall != "" { logf(0, "unsupported syscall: %v due to %v", s.Name, skipCurrentSyscall) @@ -226,7 +226,7 @@ func generateResources(desc *Description, consts map[string]uint64, out io.Write } } fmt.Fprintf(out, "\"%v\": &ResourceDesc{Name: \"%v\", Type: ", name, name) - generateArg("resource-type", underlying, nil, desc, consts, true, true, out) + generateArg("", "resource-type", underlying, nil, desc, consts, true, true, out) fmt.Fprintf(out, ", Kind: []string{") for i, k := range kind { if i != 0 { @@ -249,66 +249,90 @@ func generateResources(desc *Description, consts map[string]uint64, out io.Write fmt.Fprintf(out, "}\n") } -func generateStructs(desc *Description, consts map[string]uint64, out io.Writer) { - var structArray StructArray - for _, str := range desc.Structs { - structArray = append(structArray, str) +func generateStructEntry(str Struct, key string, name string, out io.Writer) { + typ := "StructType" + if str.IsUnion { + typ = "UnionType" + } + packed := "" + if str.Packed { + packed = ", packed: true" + } + varlen := "" + if str.Varlen { + varlen = ", varlen: true" } - sort.Sort(structArray) + align := "" + if str.Align != 0 { + align = fmt.Sprintf(", align: %v", str.Align) + } + fmt.Fprintf(out, "\"%v\": &%v{TypeCommon: TypeCommon{TypeName: \"%v\", IsOptional: %v} %v %v %v},\n", + key, typ, name, false, packed, align, varlen) +} +func generateStructFields(str Struct, key string, desc *Description, consts map[string]uint64, out io.Writer) { + typ := "StructType" + fields := "Fields" + if str.IsUnion { + typ = "UnionType" + fields = "Options" + } + fmt.Fprintf(out, "{ s := Structs[\"%v\"].(*%v)\n", key, typ) + for _, a := range str.Flds { + fmt.Fprintf(out, "s.%v = append(s.%v, ", fields, fields) + generateArg(str.Name, a[0], a[1], a[2:], desc, consts, false, true, out) + fmt.Fprintf(out, ")\n") + } + fmt.Fprintf(out, "}\n") +} + +func generateStructs(desc *Description, consts map[string]uint64, out io.Writer) { // Struct fields can refer to other structs. Go compiler won't like if // we refer to Structs map during Structs map initialization. So we do // it in 2 passes: on the first pass create types and assign them to // the map, on the second pass fill in fields. - fmt.Fprintf(out, "var Structs = map[string]Type{\n") - for _, str := range structArray { - typ := "StructType" - if str.IsUnion { - typ = "UnionType" - } - packed := "" - if str.Packed { - packed = ", packed: true" - } - varlen := "" - if str.Varlen { - varlen = ", varlen: true" + + // Since structs of the same type can be fields with different names + // of multiple other structs, we have an instance of those structs + // for each field indexed by the name of the parent struct and the + // field name. + + structMap := make(map[string]Struct) + for _, str := range desc.Structs { + if _, ok := structMap[str.Name]; ok { + failf("two structs with the same name '%v'", str.Name) } - align := "" - if str.Align != 0 { - align = fmt.Sprintf(", align: %v", str.Align) + structMap[str.Name] = str + for _, a := range str.Flds { + if innerStr, ok := desc.Structs[a[1]]; ok { + structMap[fmt.Sprintf("%v-%v", str.Name, a[0])] = innerStr + } } - fmt.Fprintf(out, "\"%v\": &%v{TypeCommon: TypeCommon{TypeName: \"%v\", IsOptional: %v} %v %v %v},\n", - str.Name, typ, str.Name, false, packed, align, varlen) + } + + fmt.Fprintf(out, "var Structs = map[string]Type{\n") + for key, str := range structMap { + keyParts := strings.Split(key, "-") + name := keyParts[len(keyParts)-1] + generateStructEntry(str, key, name, out) } fmt.Fprintf(out, "}\n") fmt.Fprintf(out, "func initStructFields() {\n") - for _, str := range structArray { - typ := "StructType" - fields := "Fields" - if str.IsUnion { - typ = "UnionType" - fields = "Options" - } - fmt.Fprintf(out, "{ s := Structs[\"%v\"].(*%v)\n", str.Name, typ) - for _, a := range str.Flds { - fmt.Fprintf(out, "s.%v = append(s.%v, ", fields, fields) - generateArg(a[0], a[1], a[2:], desc, consts, false, true, out) - fmt.Fprintf(out, ")\n") - } - fmt.Fprintf(out, "}\n") + for key, str := range structMap { + generateStructFields(str, key, desc, consts, out) } fmt.Fprintf(out, "}\n") } func generateArg( - name, typ string, + parent, name, typ string, a []string, desc *Description, consts map[string]uint64, isArg, isField bool, out io.Writer) { + origName := name name = "\"" + name + "\"" opt := false for i, v := range a { @@ -528,7 +552,7 @@ func generateArg( default: if strings.HasPrefix(typ, "unnamed") { if inner, ok := desc.Unnamed[typ]; ok { - generateArg("", inner[0], inner[1:], desc, consts, false, isField, out) + generateArg("", "", inner[0], inner[1:], desc, consts, false, isField, out) } else { failf("unknown unnamed type '%v'", typ) } @@ -536,7 +560,11 @@ func generateArg( if len(a) != 0 { failf("struct '%v' has args", typ) } - fmt.Fprintf(out, "Structs[\"%v\"]", typ) + if parent == "" { + fmt.Fprintf(out, "Structs[\"%v\"]", typ) + } else { + fmt.Fprintf(out, "Structs[\"%v-%v\"]", parent, origName) + } } else if _, ok := desc.Resources[typ]; ok { if len(a) != 0 { failf("resource '%v' has args", typ) @@ -554,7 +582,7 @@ func generateArg( func generateType(typ string, desc *Description, consts map[string]uint64) string { buf := new(bytes.Buffer) - generateArg("", typ, nil, desc, consts, false, true, buf) + generateArg("", "", typ, nil, desc, consts, false, true, buf) return buf.String() } @@ -630,12 +658,6 @@ func (a ResourceArray) Len() int { return len(a) } func (a ResourceArray) Less(i, j int) bool { return a[i].Name < a[j].Name } func (a ResourceArray) Swap(i, j int) { a[i], a[j] = a[j], a[i] } -type StructArray []Struct - -func (a StructArray) Len() int { return len(a) } -func (a StructArray) Less(i, j int) bool { return a[i].Name < a[j].Name } -func (a StructArray) Swap(i, j int) { a[i], a[j] = a[j], a[i] } - func failf(msg string, args ...interface{}) { fmt.Fprintf(os.Stderr, msg+"\n", args...) os.Exit(1) diff --git a/sysparser/lexer.go b/sysparser/lexer.go index 85f657d41..06210a584 100644 --- a/sysparser/lexer.go +++ b/sysparser/lexer.go @@ -90,13 +90,13 @@ func Parse(in io.Reader) *Description { if len(str.Flds) <= 1 { failf("union %v has only %v fields, need at least 2", str.Name, len(str.Flds)) } - fields := make(map[string]bool) - for _, f := range str.Flds { - if fields[f[0]] { - failf("duplicate filed %v in struct/union %v", f[0], str.Name) - } - fields[f[0]] = true + } + fields := make(map[string]bool) + for _, f := range str.Flds { + if fields[f[0]] { + failf("duplicate field %v in struct/union %v", f[0], str.Name) } + fields[f[0]] = true } structs[str.Name] = *str str = nil @@ -178,6 +178,13 @@ func Parse(in io.Reader) *Description { if idx := strings.IndexByte(callName, '$'); idx != -1 { callName = callName[:idx] } + fields := make(map[string]bool) + for _, a := range args { + if fields[a[0]] { + failf("duplicate arg %v in syscall %v", a[0], name) + } + fields[a[0]] = true + } syscalls = append(syscalls, Syscall{name, callName, args, ret}) case '=': // flag |
