aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrey Konovalov <andreyknvl@gmail.com>2016-10-11 20:12:15 +0200
committerGitHub <noreply@github.com>2016-10-11 20:12:15 +0200
commitcb8fcaf84c7386b129ef54d9ee5fd36b6c56bf0d (patch)
tree5ce14a1f0391e34fb97fd317e47ff9a799ccf79e
parent0c1a91b184c3cb0ae4d5d7927ad51c5cde958b22 (diff)
parent2392578fe9881369b980a20fd6f169471b50a565 (diff)
Merge pull request #81 from xairy/better-len
Refactor & improve len type handling
-rw-r--r--prog/analysis.go117
-rw-r--r--prog/encoding.go35
-rw-r--r--prog/mutation.go60
-rw-r--r--prog/mutation_test.go32
-rw-r--r--prog/prog.go50
-rw-r--r--prog/rand.go136
-rw-r--r--prog/size_test.go123
-rw-r--r--prog/validation.go6
-rw-r--r--sys/README.md48
-rw-r--r--sys/decl.go57
-rw-r--r--sys/fuse.txt12
-rw-r--r--sys/kdbus.txt34
-rw-r--r--sys/kvm.txt4
-rw-r--r--sys/sndcontrol.txt4
-rw-r--r--sys/sndseq.txt8
-rw-r--r--sys/sndtimer.txt8
-rw-r--r--sys/sys.txt19
-rw-r--r--sys/test.txt90
-rw-r--r--sysgen/sysgen.go122
-rw-r--r--sysparser/lexer.go19
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