aboutsummaryrefslogtreecommitdiffstats
path: root/prog
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2018-01-31 20:21:01 +0100
committerDmitry Vyukov <dvyukov@google.com>2018-02-01 15:20:12 +0100
commitd973f28294d93cf54de9b2b87d4cc8524bb8a28a (patch)
treea88c26c1e1de0afa0340e7213b2738f81f40bb22 /prog
parenta84dec47f0bdc461828c2903429e669fc0fa5e10 (diff)
prog: don't serialize default arguments
This reduces size of a corpus in half. We store corpus on manager and on hub, so this will reduce their memory consumption. But also makes large programs more readable.
Diffstat (limited to 'prog')
-rw-r--r--prog/encoding.go52
-rw-r--r--prog/encoding_test.go43
-rw-r--r--prog/mutation_test.go10
-rw-r--r--prog/prog.go72
-rw-r--r--prog/size_test.go2
-rw-r--r--prog/validation.go3
6 files changed, 162 insertions, 20 deletions
diff --git a/prog/encoding.go b/prog/encoding.go
index b60726c9c..0261d4772 100644
--- a/prog/encoding.go
+++ b/prog/encoding.go
@@ -71,8 +71,11 @@ func serialize(arg Arg, buf *bytes.Buffer, vars map[Arg]int, varSeq *int) {
fmt.Fprintf(buf, "0x0")
break
}
- fmt.Fprintf(buf, "&%v=", serializeAddr(arg))
- serialize(a.Res, buf, vars, varSeq)
+ fmt.Fprintf(buf, "&%v", serializeAddr(arg))
+ if a.Res == nil || !isDefaultArg(a.Res) {
+ fmt.Fprintf(buf, "=")
+ serialize(a.Res, buf, vars, varSeq)
+ }
case *DataArg:
if a.Type().Dir() == DirOut {
fmt.Fprintf(buf, "\"\"/%v", a.Size())
@@ -98,7 +101,16 @@ func serialize(arg Arg, buf *bytes.Buffer, vars map[Arg]int, varSeq *int) {
panic("unknown group type")
}
buf.Write([]byte{delims[0]})
- for i, arg1 := range a.Inner {
+ lastNonDefault := len(a.Inner) - 1
+ if a.fixedInnerSize() {
+ for ; lastNonDefault >= 0; lastNonDefault-- {
+ if !isDefaultArg(a.Inner[lastNonDefault]) {
+ break
+ }
+ }
+ }
+ for i := 0; i <= lastNonDefault; i++ {
+ arg1 := a.Inner[i]
if arg1 != nil && IsPad(arg1.Type()) {
continue
}
@@ -109,8 +121,11 @@ func serialize(arg Arg, buf *bytes.Buffer, vars map[Arg]int, varSeq *int) {
}
buf.Write([]byte{delims[1]})
case *UnionArg:
- fmt.Fprintf(buf, "@%v=", a.Option.Type().FieldName())
- serialize(a.Option, buf, vars, varSeq)
+ fmt.Fprintf(buf, "@%v", a.Option.Type().FieldName())
+ if !isDefaultArg(a.Option) {
+ fmt.Fprintf(buf, "=")
+ serialize(a.Option, buf, vars, varSeq)
+ }
case *ResultArg:
if a.Res == nil {
fmt.Fprintf(buf, "0x%x", a.Val)
@@ -277,10 +292,15 @@ func (target *Target) parseArg(typ Type, p *parser, vars map[string]Arg) (Arg, e
if err != nil {
return nil, err
}
- p.Parse('=')
- inner, err := target.parseArg(typ1, p, vars)
- if err != nil {
- return nil, err
+ var inner Arg
+ if p.Char() == '=' {
+ p.Parse('=')
+ inner, err = target.parseArg(typ1, p, vars)
+ if err != nil {
+ return nil, err
+ }
+ } else {
+ inner = defaultArg(typ1)
}
arg = MakePointerArg(typ, page, off, size, inner)
case '(':
@@ -381,7 +401,6 @@ func (target *Target) parseArg(typ Type, p *parser, vars map[string]Arg) (Arg, e
}
p.Parse('@')
name := p.Ident()
- p.Parse('=')
var optType Type
for _, t2 := range t1.Fields {
if name == t2.FieldName() {
@@ -392,9 +411,16 @@ func (target *Target) parseArg(typ Type, p *parser, vars map[string]Arg) (Arg, e
if optType == nil {
return nil, fmt.Errorf("union arg %v has unknown option: %v", typ.Name(), name)
}
- opt, err := target.parseArg(optType, p, vars)
- if err != nil {
- return nil, err
+ var opt Arg
+ if p.Char() == '=' {
+ p.Parse('=')
+ var err error
+ opt, err = target.parseArg(optType, p, vars)
+ if err != nil {
+ return nil, err
+ }
+ } else {
+ opt = defaultArg(optType)
}
arg = MakeUnionArg(typ, opt)
case 'n':
diff --git a/prog/encoding_test.go b/prog/encoding_test.go
index 6c063b5a7..da90c6bbe 100644
--- a/prog/encoding_test.go
+++ b/prog/encoding_test.go
@@ -188,3 +188,46 @@ func TestSerializeDeserialize(t *testing.T) {
}
}
}
+
+func TestSerializeDeserializeRandom(t *testing.T) {
+ testEachTargetRandom(t, func(t *testing.T, target *Target, rs rand.Source, iters int) {
+ data0 := make([]byte, ExecBufferSize)
+ data1 := make([]byte, ExecBufferSize)
+ for i := 0; i < iters; i++ {
+ p0 := target.Generate(rs, 10, nil)
+ if ok, _, _ := testSerializeDeserialize(t, p0, data0, data1); ok {
+ continue
+ }
+ p0, _ = Minimize(p0, -1, func(p1 *Prog, _ int) bool {
+ ok, _, _ := testSerializeDeserialize(t, p1, data0, data1)
+ return !ok
+ }, false)
+ ok, n0, n1 := testSerializeDeserialize(t, p0, data0, data1)
+ if ok {
+ t.Fatal("flaky?")
+ }
+ t.Fatalf("was: %q\ngot: %q\nprogram:\n%s",
+ data0[:n0], data1[:n1], p0.Serialize())
+ }
+ })
+}
+
+func testSerializeDeserialize(t *testing.T, p0 *Prog, data0, data1 []byte) (bool, int, int) {
+ n0, err := p0.SerializeForExec(data0)
+ if err != nil {
+ t.Fatal(err)
+ }
+ serialized := p0.Serialize()
+ p1, err := p0.Target.Deserialize(serialized)
+ if err != nil {
+ t.Fatal(err)
+ }
+ n1, err := p1.SerializeForExec(data1)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !bytes.Equal(data0[:n0], data1[:n1]) {
+ return false, n0, n1
+ }
+ return true, 0, 0
+}
diff --git a/prog/mutation_test.go b/prog/mutation_test.go
index 144b48d11..72f0e3cac 100644
--- a/prog/mutation_test.go
+++ b/prog/mutation_test.go
@@ -31,8 +31,8 @@ func TestMutateRandom(t *testing.T) {
data0 := p.Serialize()
p1 := p.Clone()
// There is a chance that mutation will produce the same program.
- // So we check that at least 1 out of 10 mutations actually change the program.
- for try := 0; try < 10; try++ {
+ // So we check that at least 1 out of 20 mutations actually change the program.
+ for try := 0; try < 20; try++ {
p1.Mutate(rs, 10, nil, nil)
data := p.Serialize()
if !bytes.Equal(data0, data) {
@@ -212,7 +212,7 @@ func TestMinimize(t *testing.T) {
},
"mmap(&(0x7f0000000000/0x1000)=nil, 0x1000, 0x3, 0x32, 0xffffffffffffffff, 0x0)\n" +
"sched_yield()\n" +
- "pipe2(&(0x7f0000000000)={0x0, 0x0}, 0x0)\n",
+ "pipe2(&(0x7f0000000000), 0x0)\n",
2,
},
// Remove a call.
@@ -259,7 +259,7 @@ func TestMinimize(t *testing.T) {
return p.String() == "mmap-write-sched_yield"
},
"mmap(&(0x7f0000000000/0x1000)=nil, 0x1000, 0x0, 0x0, 0xffffffffffffffff, 0x0)\n" +
- "write(0xffffffffffffffff, &(0x7f0000000000)=\"\", 0x0)\n" +
+ "write(0xffffffffffffffff, &(0x7f0000000000), 0x0)\n" +
"sched_yield()\n",
2,
},
@@ -274,7 +274,7 @@ func TestMinimize(t *testing.T) {
return p.String() == "mmap-write-sched_yield"
},
"mmap(&(0x7f0000000000/0x1000)=nil, 0x1000, 0x0, 0x0, 0xffffffffffffffff, 0x0)\n" +
- "write(0xffffffffffffffff, &(0x7f0000000000)=\"\", 0x0)\n" +
+ "write(0xffffffffffffffff, &(0x7f0000000000), 0x0)\n" +
"sched_yield()\n",
-1,
},
diff --git a/prog/prog.go b/prog/prog.go
index ce3058ba0..7decea907 100644
--- a/prog/prog.go
+++ b/prog/prog.go
@@ -189,6 +189,17 @@ func (arg *GroupArg) Size() uint64 {
}
}
+func (arg *GroupArg) fixedInnerSize() bool {
+ switch typ := arg.Type().(type) {
+ case *StructType:
+ return true
+ case *ArrayType:
+ return typ.Kind == ArrayRangeLen && typ.RangeBegin == typ.RangeEnd
+ default:
+ panic(fmt.Sprintf("bad group arg type %v", typ))
+ }
+}
+
// Used for UnionType.
type UnionArg struct {
ArgCommon
@@ -328,6 +339,67 @@ func defaultArg(t Type) Arg {
}
}
+func isDefaultArg(arg Arg) bool {
+ if IsPad(arg.Type()) {
+ return true
+ }
+ switch a := arg.(type) {
+ case *ConstArg:
+ switch t := a.Type().(type) {
+ case *IntType, *ConstType, *FlagsType, *LenType, *ProcType, *CsumType:
+ return a.Val == t.Default()
+ default:
+ panic("unknown const type")
+ }
+ case *GroupArg:
+ if !a.fixedInnerSize() {
+ return false
+ }
+ for _, elem := range a.Inner {
+ if !isDefaultArg(elem) {
+ return false
+ }
+ }
+ return true
+ case *UnionArg:
+ t := a.Type().(*UnionType)
+ return a.Option.Type().FieldName() == t.Fields[0].Name() &&
+ isDefaultArg(a.Option)
+ case *DataArg:
+ if a.Size() == 0 {
+ return true
+ }
+ if a.Type().Varlen() {
+ return false
+ }
+ if a.Type().Dir() == DirOut {
+ return true
+ }
+ for _, v := range a.Data() {
+ if v != 0 {
+ return false
+ }
+ }
+ return true
+ case *PointerArg:
+ switch t := a.Type().(type) {
+ case *PtrType:
+ return a.PageIndex == 0 && a.PageOffset == 0 && a.PagesNum == 0 &&
+ (((t.Optional() || t.Dir() == DirOut) && a.Res == nil) ||
+ (!t.Optional() && t.Dir() != DirOut && a.Res != nil && isDefaultArg(a.Res)))
+ case *VmaType:
+ return a.PageIndex == 0 && a.PageOffset == 0 && a.PagesNum == 1 && a.Res == nil
+ default:
+ panic("unknown pointer type")
+ }
+ case *ResultArg:
+ t := a.Type().(*ResourceType)
+ return a.Res == nil && a.OpDiv == 0 && a.OpAdd == 0 &&
+ len(a.uses) == 0 && a.Val == t.Desc.Type.Default()
+ }
+ return false
+}
+
func (p *Prog) insertBefore(c *Call, calls []*Call) {
idx := 0
for ; idx < len(p.Calls); idx++ {
diff --git a/prog/size_test.go b/prog/size_test.go
index 2ab28b90b..5ea646ca7 100644
--- a/prog/size_test.go
+++ b/prog/size_test.go
@@ -120,7 +120,7 @@ func TestAssignSize(t *testing.T) {
},
{
"syz_test$length21(&(0x7f0000000000)=0x0, 0x0)",
- "syz_test$length21(&(0x7f0000000000)=0x0, 0x40)",
+ "syz_test$length21(&(0x7f0000000000), 0x40)",
},
{
"syz_test$length22(&(0x7f0000000000)='12345', 0x0)",
diff --git a/prog/validation.go b/prog/validation.go
index 3ed9eabc2..400b13140 100644
--- a/prog/validation.go
+++ b/prog/validation.go
@@ -42,7 +42,8 @@ func (c *Call) validate(ctx *validCtx) error {
return fmt.Errorf("syscall %v: nil arg", c.Meta.Name)
}
if ctx.args[arg] {
- return fmt.Errorf("syscall %v: arg is referenced several times in the tree", c.Meta.Name)
+ return fmt.Errorf("syscall %v: arg %#v is referenced several times in the tree",
+ c.Meta.Name, arg)
}
ctx.args[arg] = true
if used, ok := arg.(ArgUsed); ok {