diff options
| author | Dmitry Vyukov <dvyukov@google.com> | 2018-01-31 20:21:01 +0100 |
|---|---|---|
| committer | Dmitry Vyukov <dvyukov@google.com> | 2018-02-01 15:20:12 +0100 |
| commit | d973f28294d93cf54de9b2b87d4cc8524bb8a28a (patch) | |
| tree | a88c26c1e1de0afa0340e7213b2738f81f40bb22 /prog | |
| parent | a84dec47f0bdc461828c2903429e669fc0fa5e10 (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.go | 52 | ||||
| -rw-r--r-- | prog/encoding_test.go | 43 | ||||
| -rw-r--r-- | prog/mutation_test.go | 10 | ||||
| -rw-r--r-- | prog/prog.go | 72 | ||||
| -rw-r--r-- | prog/size_test.go | 2 | ||||
| -rw-r--r-- | prog/validation.go | 3 |
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 { |
