blob: e2abacee1d5abc566deca75ad041ff5adabaf0f6 [file] [log] [blame]
// 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"
"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 _, readable := range []bool{false, true} {
for i := 0; i < 1e3; i++ {
data := make([]byte, r.Intn(4))
for i := range data {
data[i] = byte(r.Intn(256))
}
buf := new(bytes.Buffer)
serializeData(buf, data, readable)
p := newParser(nil, buf.Bytes(), true)
if !p.Scan() {
t.Fatalf("parser does not scan")
}
data1, _, err := p.deserializeData()
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) {
t.Parallel()
tests := []struct {
prog string
ok bool
calls []string
ncalls int
}{
{
"",
false,
[]string{},
0,
},
{
"r0 = (foo)",
false,
[]string{},
0,
},
{
"getpid()",
true,
[]string{"getpid"},
1,
},
{
"r11 = getpid()",
true,
[]string{"getpid"},
1,
},
{
"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"},
4,
},
}
for i, test := range tests {
t.Run(fmt.Sprint(i), func(t *testing.T) {
calls, ncalls, 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)
}
if ncalls != test.ncalls {
t.Fatalf("got %v calls, expect %v", ncalls, test.ncalls)
}
})
}
}
func TestCallSetRandom(t *testing.T) {
target, rs, iters := initTest(t)
ct := target.DefaultChoiceTable()
for i := 0; i < iters; i++ {
const ncalls = 10
p := target.Generate(rs, ncalls, ct)
calls0 := make(map[string]struct{})
for _, c := range p.Calls {
calls0[c.Meta.Name] = struct{}{}
}
calls1, ncalls1, 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)
}
if ncalls1 != ncalls {
t.Fatalf("got %v calls, expect %v", ncalls1, ncalls)
}
}
}
func TestDeserialize(t *testing.T) {
TestDeserializeHelper(t, "test", "64", nil, []DeserializeTest{
{
In: `test$struct(&(0x7f0000000000)={0x0, {0x0}})`,
Out: `test$struct(&(0x7f0000000000))`,
},
{
In: `test$struct(&(0x7f0000000000)=0x0)`,
Out: `test$struct(&(0x7f0000000000))`,
StrictErr: "wrong int arg",
},
{
In: `test$regression1(&(0x7f0000000000)=[{"000000"}, {"0000000000"}])`,
Out: `test$regression1(&(0x7f0000000000)=[{}, {}])`,
},
{
In: `test$regression2(&(0x7f0000000000)=[0x1, 0x2, 0x3, 0x4, 0x5, 0x6])`,
Out: `test$regression2(&(0x7f0000000000)=[0x1, 0x2, 0x3, 0x4])`,
},
{
In: `test_excessive_args1(0x0, 0x1, {0x1, &(0x7f0000000000)=[0x1, 0x2]})`,
Out: `test_excessive_args1()`,
StrictErr: "excessive syscall arguments",
},
{
In: `test_excessive_args2(0x0, 0x1, {0x1, &(0x7f0000000000)={0x1, 0x2}})`,
Out: `test_excessive_args2(0x0)`,
StrictErr: "excessive syscall arguments",
},
{
In: `test_excessive_args2(0x0, 0x1, {0x1, &(0x7f0000000000)=nil})`,
Out: `test_excessive_args2(0x0)`,
StrictErr: "excessive syscall arguments",
},
{
In: `test_excessive_args2(0x0, &(0x7f0000000000), 0x0)`,
Out: `test_excessive_args2(0x0)`,
StrictErr: "excessive syscall arguments",
},
{
In: `test$excessive_fields1(&(0x7f0000000000)={0x1, &(0x7f0000000000)=[{0x0}, 0x2]}, {0x1, 0x2, [0x1, 0x2]})`,
Out: `test$excessive_fields1(&(0x7f0000000000)={0x1})`,
StrictErr: "excessive struct excessive_fields fields",
},
{
In: `test$excessive_fields1(0x0)`,
},
{
In: `test$excessive_fields1(r0)`,
Out: `test$excessive_fields1(&(0x7f0000000000))`,
StrictErr: "undeclared variable r0",
},
{
In: `test_excessive_args2(r1)`,
Out: `test_excessive_args2(0x0)`,
StrictErr: "undeclared variable r1",
},
{
In: `test_excessive_args2({0x0, 0x1})`,
Out: `test_excessive_args2(0x0)`,
StrictErr: "wrong struct arg",
},
{
In: `test_excessive_args2([0x0], 0x0)`,
Out: `test_excessive_args2(0x0)`,
StrictErr: "wrong array arg",
},
{
In: `test_excessive_args2(@foo)`,
Out: `test_excessive_args2(0x0)`,
StrictErr: "wrong union arg",
},
{
In: `test_excessive_args2('foo')`,
Out: `test_excessive_args2(0x0)`,
StrictErr: "wrong string arg",
},
{
In: `test_excessive_args2(&(0x7f0000000000)={0x0, 0x1})`,
Out: `test_excessive_args2(0x0)`,
StrictErr: "wrong addr arg",
},
{
In: `test_excessive_args2(nil)`,
Out: `test_excessive_args2(0x0)`,
},
{
In: `test$type_confusion1(&(0x7f0000000000)=@unknown)`,
Out: `test$type_confusion1(&(0x7f0000000000))`,
StrictErr: "wrong union option",
},
{
In: `test$type_confusion1(&(0x7f0000000000)=@unknown={0x0, 'abc'}, 0x0)`,
Out: `test$type_confusion1(&(0x7f0000000000))`,
StrictErr: "wrong union option",
},
{
In: `test$excessive_fields1(&(0x7f0000000000)=0x0)`,
Out: `test$excessive_fields1(&(0x7f0000000000))`,
StrictErr: "wrong int arg",
},
{
In: `test$excessive_fields1(0xffffffffffffffff)`,
},
{
In: `test$excessive_fields1(0xfffffffffffffffe)`,
},
{
In: `test$excessive_fields1(0xfffffffffffffffd)`,
Out: `test$excessive_fields1(0x0)`,
},
{
In: `test$excessive_fields1(0xfffffffffffffffc)`,
Out: `test$excessive_fields1(0xffffffffffffffff)`,
},
{
In: `test$auto0(AUTO, &AUTO={AUTO, AUTO, 0x1}, AUTO, 0x0)`,
Out: `test$auto0(0x42, &(0x7f0000000040)={0xc, 0x43, 0x1}, 0xc, 0x0)`,
},
{
In: `test$auto0(AUTO, &AUTO={AUTO, AUTO, AUTO}, AUTO, 0x0)`,
Err: `wrong type *prog.IntType for AUTO`,
},
{
In: `test$auto1(AUTO, &AUTO=AUTO, AUTO, 0x0)`,
Out: `test$auto1(0x42, &(0x7f0000000040)={0xc, 0x43, 0x0}, 0xc, 0x0)`,
},
{
In: `test$auto2(AUTO, &AUTO=AUTO, AUTO, 0x0)`,
Out: `test$auto2(0x42, &(0x7f0000000040)={0x10, {0xc, 0x43, 0x0}}, 0x10, 0x0)`,
},
{
In: `test$auto0(AUTO, &AUTO=AUTO, AUTO, 0x0)`,
Err: `wrong type *prog.IntType for AUTO`,
},
{
In: `test$bf2(&AUTO={AUTO, 0x10, 0x0, AUTO})`,
Out: `test$bf2(&(0x7f0000000040)={0x8, 0x10, 0x0, 0x18})`,
},
{
In: `test$str0(&AUTO="303100090a0d7022273a")`,
Out: `test$str0(&(0x7f0000000040)='01\x00\t\n\rp\"\':')`,
},
{
In: `test$blob0(&AUTO="303100090a0d7022273a")`,
Out: `test$blob0(&(0x7f0000000040)='01\x00\t\n\rp\"\':')`,
},
{
In: `test$blob0(&AUTO="3031000a0d7022273a01")`,
Out: `test$blob0(&(0x7f0000000040)="3031000a0d7022273a01")`,
},
{
In: `test$out_const(&(0x7f0000000000)=0x2)`,
Out: `test$out_const(&(0x7f0000000000))`,
StrictErr: `out arg const[1, const] has non-default value: 2`,
},
{
In: `test$str1(&(0x7f0000000000)='foo\x00')`,
Out: `test$str1(&(0x7f0000000000))`,
},
{
In: `test$str1(&(0x7f0000000000)='bar\x00')`,
Out: `test$str1(&(0x7f0000000000))`,
StrictErr: `bad string value "bar\x00", expect ["foo\x00"]`,
},
{
In: `test$str2(&(0x7f0000000000)='bar\x00')`,
},
{
In: `test$str2(&(0x7f0000000000)='baz\x00')`,
Out: `test$str2(&(0x7f0000000000)='foo\x00')`,
StrictErr: `bad string value "baz\x00", expect ["foo\x00" "bar\x00"]`,
},
{
In: `test$opt2(&(0x7f0000000000))`,
Out: `test$opt2(0x0)`,
},
{
In: `test$opt2(&(0x7f0000000001))`,
Out: `test$opt2(0x0)`,
StrictErr: `unaligned vma address 0x1`,
},
{
In: `test$opt2(&(0x7f0000000000)=nil)`,
Out: `test$opt2(0x0)`,
},
{
In: `test$opt2(&(0x7f0000000000)='foo')`,
Out: `test$opt2(0x0)`,
StrictErr: `non-nil argument for nil type`,
},
{
In: `test$opt2(0x0) (non_existing_prop: 123)`,
Out: `test$opt2(0x0)`,
StrictErr: `unknown call property: non_existing_prop`,
},
{
In: `test$opt2(0x0) (fail_nth: zzz)`,
Out: `test$opt2(0x0)`,
StrictErr: `invalid int value: zzz`,
},
{
In: `test$opt2(0x0) (non_existing_prop: 123, fail_nth: 1)`,
Out: `test$opt2(0x0) (fail_nth: 1)`,
StrictErr: `unknown call property: non_existing_prop`,
},
{
In: `test$opt2(0x0) (fail_nth: 0)`,
Out: `test$opt2(0x0)`,
},
{
In: `test$str2(&(0x7f0000000000)="$eJwqrqzKTszJSS0CBAAA//8TyQPi`,
Err: `want ", got EOF`,
},
{
In: `mutate9(&(0x7f0000000000)='./local/filename\x00')`,
},
{
In: `mutate9(&(0x7f0000000000)='/escaping/filename\x00')`,
Err: `escaping filename`,
},
})
}
func TestSerializeDeserialize(t *testing.T) {
TestDeserializeHelper(t, "test", "64", nil, []DeserializeTest{
{
In: `serialize0(&(0x7f0000408000)={"6861736800000000000000000000", "48490000"})`,
Out: `serialize0(&(0x7f0000408000)={'hash\x00', 'HI\x00'})`,
},
{
In: `serialize1(&(0x7f0000000000)="0000000000000000", 0x8)`,
Out: `serialize1(&(0x7f0000000000)=""/8, 0x8)`,
},
{
In: `serialize2(&(0x7f0000000000)="$c3l6a2FsbGVy")`,
Out: `serialize2(&(0x7f0000000000)='syzkaller')`,
},
{
In: `serialize3(&(0x7f0000000000)="$eJwqrqzKTszJSS0CBAAA//8TyQPi")`,
},
})
}
func TestSerializeDeserializeRandom(t *testing.T) {
testEachTargetRandom(t, func(t *testing.T, target *Target, rs rand.Source, iters int) {
ct := target.DefaultChoiceTable()
data0 := make([]byte, ExecBufferSize)
data1 := make([]byte, ExecBufferSize)
for i := 0; i < iters; i++ {
p0 := target.Generate(rs, 10, ct)
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.Log("flaky?")
}
decoded0, err := target.DeserializeExec(data0[:n0])
if err != nil {
t.Fatal(err)
}
decoded1, err := target.DeserializeExec(data1[:n1])
if err != nil {
t.Fatal(err)
}
t.Logf("decoded0: %+v", decoded0)
t.Logf("decoded1: %+v", decoded1)
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, NonStrict)
if err != nil {
t.Fatal(err)
}
n1, err := p1.SerializeForExec(data1)
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(data0[:n0], data1[:n1]) {
t.Logf("PROG0:\n%s\n", p0.Serialize())
t.Logf("PROG1:\n%s\n", p1.Serialize())
return false, n0, n1
}
return true, 0, 0
}
func TestSerializeCallProps(t *testing.T) {
target := initTargetTest(t, "test", "64")
type SerializeCallPropsTest struct {
prog string
props []CallProps
}
tests := []SerializeCallPropsTest{
{
"serialize0(0x0)\n",
[]CallProps{{}},
},
{
"serialize0(0x0) ()\n",
[]CallProps{{}},
},
{
"serialize0(0x0) (fail_nth: 5)\n",
[]CallProps{{5, false, 0}},
},
{
"serialize0(0x0) (fail_nth)\n",
nil,
},
{
"serialize0(0x0) (fail_nth: \"5\")\n",
nil,
},
{
"serialize0(0x0) (async)\n",
[]CallProps{{0, true, 0}},
},
{
"serialize0(0x0) (async, rerun: 10)\n",
[]CallProps{{0, true, 10}},
},
}
for _, test := range tests {
p, err := target.Deserialize([]byte(test.prog), Strict)
if test.props != nil && err != nil {
t.Fatal(err)
} else if test.props == nil && err == nil {
t.Errorf("expected an error, but got none\n%s", test.prog)
}
for i, props := range test.props {
if !reflect.DeepEqual(props, p.Calls[i].Props) {
t.Errorf("%v-th call props differ: %v != %v", i, props, p.Calls[i].Props)
}
}
}
}
func TestDeserializeComments(t *testing.T) {
target := initTargetTest(t, "test", "64")
p, err := target.Deserialize([]byte(`
# comment1
# comment2
serialize0(0x0)
serialize0(0x0)
# comment3
serialize0(0x0)
# comment4
serialize0(0x0) # comment5
#comment6
serialize0(0x0)
#comment7
`), Strict)
if err != nil {
t.Fatal(err)
}
for i, want := range []string{
"comment2",
"",
"comment3",
"comment5",
"",
} {
if got := p.Calls[i].Comment; got != want {
t.Errorf("bad call %v comment: %q, want %q", i, got, want)
}
}
wantComments := []string{
"comment1",
"comment4",
"comment6",
"comment7",
}
if !reflect.DeepEqual(p.Comments, wantComments) {
t.Errorf("bad program comments %q\nwant: %q", p.Comments, wantComments)
}
}
func TestHasNext(t *testing.T) {
testCases := []struct {
input string
expected bool
}{
{"abcdef", true},
{"xyz", false},
{"ab", false},
{"abc", true},
}
for _, testCase := range testCases {
p := newParser(nil, []byte(testCase.input), true)
if !p.Scan() {
t.Fatalf("parser does not scan")
}
result := p.HasNext("abc")
if result != testCase.expected {
t.Errorf("expected HasNext(\"abc\") on input %q to be %v, but got %v",
testCase.input, testCase.expected, result)
}
}
}