blob: 0e29777a792c17599bafe9dc585fa429faad3fc0 [file] [log] [blame] [edit]
// Copyright 2021 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 (
"math/rand"
"testing"
"github.com/stretchr/testify/assert"
)
func TestAssignRandomAsync(t *testing.T) {
tests := []struct {
os string
arch string
orig string
check func(*Prog) bool
}{
{
"linux", "amd64",
`r0 = openat(0xffffffffffffff9c, &AUTO='./file1\x00', 0x42, 0x1ff)
write(r0, &AUTO="01010101", 0x4)
read(r0, &AUTO=""/4, 0x4)
close(r0)
`,
func(p *Prog) bool {
return !p.Calls[0].Props.Async
},
},
{
"linux", "amd64",
`r0 = openat(0xffffffffffffff9c, &AUTO='./file1\x00', 0x42, 0x1ff)
nanosleep(&AUTO={0x0,0x4C4B40}, &AUTO={0,0})
write(r0, &AUTO="01010101", 0x4)
read(r0, &AUTO=""/4, 0x4)
close(r0)
`,
func(p *Prog) bool {
return !p.Calls[0].Props.Async || !p.Calls[1].Props.Async
},
},
{
"linux", "amd64",
`r0 = openat(0xffffffffffffff9c, &AUTO='./file1\x00', 0x42, 0x1ff)
r1 = dup(r0)
r2 = dup(r1)
r3 = dup(r2)
r4 = dup(r3)
`,
func(p *Prog) bool {
for _, call := range p.Calls[0 : len(p.Calls)-1] {
if call.Props.Async {
return false
}
}
return true
},
},
}
_, rs, iters := initTest(t)
r := rand.New(rs)
anyAsync := false
for _, test := range tests {
target, err := GetTarget(test.os, test.arch)
if err != nil {
t.Fatal(err)
}
p, err := target.Deserialize([]byte(test.orig), Strict)
if err != nil {
t.Fatal(err)
}
for i := 0; i < iters; i++ {
collided := AssignRandomAsync(p, r)
if !test.check(collided) {
t.Fatalf("bad async assignment:\n%s\n", collided.Serialize())
}
for _, call := range collided.Calls {
anyAsync = anyAsync || call.Props.Async
}
}
}
if !anyAsync {
t.Fatalf("not a single async was assigned")
}
}
func TestDoubleExecCollide(t *testing.T) {
tests := []struct {
os string
arch string
orig string
duplicated string
shouldFail bool
}{
{
"linux", "amd64",
`r0 = openat(0xffffffffffffff9c, &AUTO='./file1\x00', 0x42, 0x1ff)
r1 = dup(r0)
r2 = dup(r1)
r3 = dup(r2)
r4 = dup(r2)
r5 = dup(r3)
`,
`r0 = openat(0xffffffffffffff9c, &(0x7f0000000040)='./file1\x00', 0x42, 0x1ff)
r1 = dup(r0)
r2 = dup(r1)
r3 = dup(r2)
dup(r2)
dup(r3)
openat(0xffffffffffffff9c, &(0x7f0000000040)='./file1\x00', 0x42, 0x1ff)
dup(r0)
dup(r1)
dup(r2)
dup(r2)
dup(r3)
`,
false,
},
}
_, rs, iters := initTest(t)
r := rand.New(rs)
for _, test := range tests {
target, err := GetTarget(test.os, test.arch)
if err != nil {
t.Fatal(err)
}
p, err := target.Deserialize([]byte(test.orig), Strict)
if err != nil {
t.Fatal(err)
}
for i := 0; i < iters; i++ {
collided, err := DoubleExecCollide(p, r)
if test.shouldFail && err == nil {
t.Fatalf("expected to fail, but it hasn't")
} else if !test.shouldFail && err != nil {
t.Fatalf("unexpected error: %s", err)
}
if test.duplicated != "" {
woProps := collided.Clone()
for _, c := range woProps.Calls {
c.Props = CallProps{}
}
serialized := string(woProps.Serialize())
if serialized != test.duplicated {
t.Fatalf("expected:%s\ngot:%s\n", test.duplicated, serialized)
}
}
// TODO: also test the `async` assignment.
}
}
}
func TestDupCallCollide(t *testing.T) {
tests := []struct {
os string
arch string
orig string
rets []string
}{
{
"linux", "amd64",
`r0 = openat(0xffffffffffffff9c, &AUTO='./file1\x00', 0x42, 0x1ff)
r1 = dup(r0)
r2 = dup(r1)
dup(r2)
`,
[]string{
`r0 = openat(0xffffffffffffff9c, &(0x7f0000000040)='./file1\x00', 0x42, 0x1ff)
dup(r0) (async)
r1 = dup(r0)
r2 = dup(r1)
dup(r2)
`,
`r0 = openat(0xffffffffffffff9c, &(0x7f0000000040)='./file1\x00', 0x42, 0x1ff)
r1 = dup(r0)
r2 = dup(r1)
dup(r2) (async)
dup(r2)
`,
},
},
}
_, rs, iters := initTest(t)
if iters > 100 {
// Let's save resources -- we don't need that many for these small tests.
iters = 100
}
r := rand.New(rs)
for _, test := range tests {
target, err := GetTarget(test.os, test.arch)
if err != nil {
t.Fatal(err)
}
p, err := target.Deserialize([]byte(test.orig), Strict)
if err != nil {
t.Fatal(err)
}
detected := map[string]struct{}{}
for i := 0; i < iters; i++ {
collided, err := DupCallCollide(p, r)
assert.NoError(t, err)
detected[string(collided.Serialize())] = struct{}{}
}
for _, variant := range test.rets {
_, exists := detected[variant]
assert.True(t, exists)
}
}
}