aboutsummaryrefslogtreecommitdiffstats
path: root/prog
diff options
context:
space:
mode:
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)
+ }
+ }
+}