aboutsummaryrefslogtreecommitdiffstats
path: root/prog
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2017-12-08 10:45:11 +0100
committerDmitry Vyukov <dvyukov@google.com>2017-12-08 10:45:11 +0100
commit4016fc5ad7f3a4760c28fa7c6c3c1fa30e2ba1de (patch)
tree037d7746187ebe8cd7470cf28f6f124e6c5187f1 /prog
parent5c1e6a291b4c23d3ace93651996d989be3b7939d (diff)
prog: fix hints of data args
Hints for data args don't work. We do all the work, but at the final stage we patch arg in the _old_ program, not in the _new_ one. So programs passed to the callback are all the same and don't contain any mutations. Tests did not catch this because they work right before that point (don't test the actual interface function MutateWithHints). Fix that and add a test that catches this.
Diffstat (limited to 'prog')
-rw-r--r--prog/hints.go30
-rw-r--r--prog/hints_test.go71
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)
+ }
+ }
+}