syzkaller/prog/encoding_test.go
Dmitry Vyukov dd768bf1c6 prog: reorder Minimize arguments
Make the predicate the last argument.
It's more common and convenient (arguments are not separated by multiple lines).
2018-02-19 21:48:20 +01:00

234 lines
5.3 KiB
Go

// Copyright 2016 syzkaller project authors. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
package prog
import (
"bytes"
"fmt"
"math/rand"
"reflect"
"regexp"
"sort"
"testing"
)
func setToArray(s map[string]struct{}) []string {
a := make([]string, 0, len(s))
for c := range s {
a = append(a, c)
}
sort.Strings(a)
return a
}
func TestSerializeData(t *testing.T) {
t.Parallel()
r := rand.New(rand.NewSource(0))
for i := 0; i < 1e4; i++ {
data := make([]byte, r.Intn(4))
for i := range data {
data[i] = byte(r.Intn(256))
}
buf := new(bytes.Buffer)
serializeData(buf, data)
p := newParser(buf.Bytes())
if !p.Scan() {
t.Fatalf("parser does not scan")
}
data1, err := deserializeData(p)
if err != nil {
t.Fatalf("failed to deserialize %q -> %s: %v", data, buf.Bytes(), err)
}
if !bytes.Equal(data, data1) {
t.Fatalf("corrupted data %q -> %s -> %q", data, buf.Bytes(), data1)
}
}
}
func TestCallSet(t *testing.T) {
tests := []struct {
prog string
ok bool
calls []string
}{
{
"",
false,
[]string{},
},
{
"r0 = (foo)",
false,
[]string{},
},
{
"getpid()",
true,
[]string{"getpid"},
},
{
"r11 = getpid()",
true,
[]string{"getpid"},
},
{
"getpid()\n" +
"open(0x1, something that this package may not understand)\n" +
"getpid()\n" +
"#read()\n" +
"\n" +
"close$foo(&(0x0000) = {})\n",
true,
[]string{"getpid", "open", "close$foo"},
},
}
for i, test := range tests {
t.Run(fmt.Sprint(i), func(t *testing.T) {
calls, err := CallSet([]byte(test.prog))
if err != nil && test.ok {
t.Fatalf("parsing failed: %v", err)
}
if err == nil && !test.ok {
t.Fatalf("parsing did not fail")
}
callArray := setToArray(calls)
sort.Strings(test.calls)
if !reflect.DeepEqual(callArray, test.calls) {
t.Fatalf("got call set %+v, expect %+v", callArray, test.calls)
}
})
}
}
func TestCallSetRandom(t *testing.T) {
target, rs, iters := initTest(t)
for i := 0; i < iters; i++ {
p := target.Generate(rs, 10, nil)
calls0 := make(map[string]struct{})
for _, c := range p.Calls {
calls0[c.Meta.Name] = struct{}{}
}
calls1, err := CallSet(p.Serialize())
if err != nil {
t.Fatalf("CallSet failed: %v", err)
}
callArray0 := setToArray(calls0)
callArray1 := setToArray(calls1)
if !reflect.DeepEqual(callArray0, callArray1) {
t.Fatalf("got call set:\n%+v\nexpect:\n%+v", callArray1, callArray0)
}
}
}
func TestDeserialize(t *testing.T) {
target := initTargetTest(t, "test", "64")
tests := []struct {
data string
err *regexp.Regexp
}{
{
"syz_test$struct(&(0x7f0000000000)={0x0, {0x0}})",
nil,
},
{
"syz_test$struct(&(0x7f0000000000)=0x0)",
regexp.MustCompile(`bad const type.*`),
},
{
`syz_test$regression1(&(0x7f0000000000)=[{"000000"}, {"0000000000"}])`,
nil,
},
{
`syz_test$regression2(&(0x7f0000000000)=[0x1, 0x2, 0x3, 0x4, 0x5, 0x6])`,
nil,
},
}
buf := make([]byte, ExecBufferSize)
for _, test := range tests {
p, err := target.Deserialize([]byte(test.data))
if err != nil {
if test.err == nil {
t.Fatalf("deserialization failed with\n%s\ndata:\n%s\n", err, test.data)
}
if !test.err.MatchString(err.Error()) {
t.Fatalf("deserialization failed with\n%s\nwhich doesn't match\n%s\ndata:\n%s\n", err, test.err, test.data)
}
} else {
if test.err != nil {
t.Fatalf("deserialization should have failed with:\n%s\ndata:\n%s\n",
test.err, test.data)
}
p.SerializeForExec(buf)
}
}
}
func TestSerializeDeserialize(t *testing.T) {
target := initTargetTest(t, "test", "64")
tests := [][2]string{
{
`serialize0(&(0x7f0000408000)={"6861736800000000000000000000", "4849000000"})`,
`serialize0(&(0x7f0000408000)={'hash\x00', 'HI\x00'})`,
},
{
`serialize1(&(0x7f0000000000)="0000000000000000", 0x8)`,
`serialize1(&(0x7f0000000000)=""/8, 0x8)`,
},
}
for _, test := range tests {
p, err := target.Deserialize([]byte(test[0]))
if err != nil {
t.Fatal(err)
}
data := p.Serialize()
test[1] += "\n"
if string(data) != test[1] {
t.Fatalf("\ngot : %s\nwant: %s", data, test[1])
}
}
}
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, false, func(p1 *Prog, _ int) bool {
ok, _, _ := testSerializeDeserialize(t, p1, data0, data1)
return !ok
})
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
}