diff options
Diffstat (limited to 'prog')
| -rw-r--r-- | prog/hints.go | 30 | ||||
| -rw-r--r-- | prog/hints_test.go | 71 |
2 files changed, 89 insertions, 12 deletions
diff --git a/prog/hints.go b/prog/hints.go index a4eb7ba36..065e9f8ba 100644 --- a/prog/hints.go +++ b/prog/hints.go @@ -19,7 +19,9 @@ package prog // For more insights on particular mutations please see prog/hints_test.go. import ( + "bytes" "encoding/binary" + "fmt" ) type uint64Set map[uint64]bool @@ -45,6 +47,20 @@ func (m CompMap) AddComp(arg1, arg2 uint64) { m[arg1][arg2] = true } +func (m CompMap) String() string { + buf := new(bytes.Buffer) + for v, comps := range m { + if len(buf.Bytes()) != 0 { + fmt.Fprintf(buf, ", ") + } + fmt.Fprintf(buf, "0x%x:", v) + for c := range comps { + fmt.Fprintf(buf, " 0x%x", c) + } + } + return buf.String() +} + // Mutates the program using the comparison operands stored in compMaps. // For each of the mutants executes the exec callback. func (p *Prog) MutateWithHints(callIndex int, comps CompMap, exec func(newP *Prog)) { @@ -77,14 +93,13 @@ func generateHints(p *Prog, compMap CompMap, c *Call, arg Arg, exec func(p *Prog } newP, argMap := p.cloneImpl(true) - var originalArg Arg validateExec := func() { if err := newP.validate(); err != nil { - panic("a program generated with hints did not pass validation: " + - err.Error()) + panic(fmt.Sprintf("invalid hints candidate: %v", err)) } exec(newP) } + var originalArg Arg constArgCandidate := func(newArg Arg) { oldArg := argMap[arg] newP.replaceArg(c, oldArg, newArg, nil) @@ -92,7 +107,7 @@ func generateHints(p *Prog, compMap CompMap, c *Call, arg Arg, exec func(p *Prog newP.replaceArg(c, oldArg, originalArg, nil) } - dataArgCandidate := func(newArg Arg) { + dataArgCandidate := func() { // Data arg mutations are done in-place. No need to restore the original // value - it gets restored in checkDataArg(). // dataArgCandidate is only needed for unit tests. @@ -104,8 +119,7 @@ func generateHints(p *Prog, compMap CompMap, c *Call, arg Arg, exec func(p *Prog originalArg = MakeConstArg(a.Type(), a.Val) checkConstArg(a, compMap, constArgCandidate) case *DataArg: - originalArg = MakeDataArg(a.Type(), a.Data) - checkDataArg(a, compMap, dataArgCandidate) + checkDataArg(argMap[arg].(*DataArg), compMap, dataArgCandidate) } } @@ -115,7 +129,7 @@ func checkConstArg(arg *ConstArg, compMap CompMap, cb func(newArg Arg)) { } } -func checkDataArg(arg *DataArg, compMap CompMap, cb func(newArg Arg)) { +func checkDataArg(arg *DataArg, compMap CompMap, cb func()) { bytes := make([]byte, 8) original := make([]byte, 8) for i := 0; i < min(len(arg.Data), maxDataLength); i++ { @@ -124,7 +138,7 @@ func checkDataArg(arg *DataArg, compMap CompMap, cb func(newArg Arg)) { for replacer := range shrinkExpand(val, compMap) { binary.LittleEndian.PutUint64(bytes, replacer) copy(arg.Data[i:], bytes) - cb(arg) + cb() copy(arg.Data[i:], original) } } diff --git a/prog/hints_test.go b/prog/hints_test.go index 9e5c88343..b5e35c9c9 100644 --- a/prog/hints_test.go +++ b/prog/hints_test.go @@ -4,8 +4,10 @@ package prog import ( + "encoding/hex" "fmt" "reflect" + "sort" "testing" ) @@ -26,6 +28,7 @@ type DataArgTest struct { // Tests checkConstArg(). Is not intended to check correctness of any mutations. // Mutation are checked in their own tests. func TestHintsCheckConstArg(t *testing.T) { + t.Parallel() var tests = []ConstArgTest{ { "One replacer test", @@ -66,6 +69,7 @@ func TestHintsCheckConstArg(t *testing.T) { // Tests checkDataArg(). Is not intended to check correctness of any mutations. // Mutation are checked in their own tests. func TestHintsCheckDataArg(t *testing.T) { + t.Parallel() // All inputs are in Little-Endian. var tests = []DataArgTest{ { @@ -157,10 +161,9 @@ func TestHintsCheckDataArg(t *testing.T) { // Whatever type here. It's just needed to pass the // dataArg.Type().Dir() == DirIn check. typ := ArrayType{TypeCommon{"", "", 0, DirIn, false}, nil, 0, 0, 0} - argCommon := ArgCommon{&typ} - dataArg := &DataArg{argCommon, []byte(test.in)} - checkDataArg(dataArg, test.comps, func(arg Arg) { - res[string(arg.(*DataArg).Data)] = true + dataArg := &DataArg{ArgCommon{&typ}, []byte(test.in)} + checkDataArg(dataArg, test.comps, func() { + res[string(dataArg.Data)] = true }) if !reflect.DeepEqual(res, test.res) { s := "\ngot: [" @@ -179,6 +182,7 @@ func TestHintsCheckDataArg(t *testing.T) { } func TestHintsShrinkExpand(t *testing.T) { + t.Parallel() // Naming conventions: // b - byte variable (i8 or u8) // w - word variable (i16 or u16) @@ -387,3 +391,62 @@ func extractValues(c *Call) map[uint64]bool { }) return vals } + +func TestHintsData(t *testing.T) { + t.Parallel() + type Test struct { + in string + comps CompMap + out []string + } + tests := []Test{ + { + in: "0809101112131415", + comps: CompMap{0x12111009: uint64Set{0x10: true}}, + out: []string{"0810000000131415"}, + }, + } + target, err := GetTarget("linux", "amd64") + if err != nil { + t.Fatal(err) + } + var call *Syscall + for _, c := range target.Syscalls { + if c.Name == "syz_test$hint_data" { + call = c + break + } + } + if call == nil { + t.Fatalf("can't find syz_test$hint_data") + } + for _, test := range tests { + input, err := hex.DecodeString(test.in) + if err != nil { + t.Fatal(err) + } + p := &Prog{ + Target: target, + Calls: []*Call{{ + Meta: call, + Args: []Arg{MakePointerArg(call.Args[0], 0, 0, 0, + MakeDataArg(call.Args[0].(*PtrType).Type, input))}, + Ret: MakeReturnArg(call.Ret), + }}, + } + if err := p.validate(); err != nil { + t.Fatal(err) + } + var got []string + p.MutateWithHints(0, test.comps, func(newP *Prog) { + got = append(got, hex.EncodeToString( + newP.Calls[0].Args[0].(*PointerArg).Res.(*DataArg).Data)) + }) + sort.Strings(test.out) + sort.Strings(got) + if !reflect.DeepEqual(got, test.out) { + t.Fatalf("comps: %s\ninput: %v\ngot : %+v\nwant: %+v", + test.comps, test.in, got, test.out) + } + } +} |
