syzkaller/prog/mutation_test.go
2015-10-12 10:16:57 +02:00

269 lines
8.3 KiB
Go

// Copyright 2015 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"
"testing"
)
func TestClone(t *testing.T) {
rs, iters := initTest(t)
for i := 0; i < iters; i++ {
p := Generate(rs, 10, nil)
p1 := p.Clone()
data := p.Serialize()
data1 := p1.Serialize()
if !bytes.Equal(data, data1) {
t.Fatalf("program changed after clone\noriginal:\n%s\n\nnew:\n%s\n", data, data1)
}
}
}
func TestMutate(t *testing.T) {
rs, iters := initTest(t)
next:
for i := 0; i < iters; i++ {
p := Generate(rs, 10, nil)
data0 := p.Serialize()
p1 := p.Clone()
// There is a chance that mutation will produce the same program.
// So we check that at least 1 out of 10 mutations actually change the program.
for try := 0; try < 10; try++ {
p1.Mutate(rs, 10, nil)
data := p.Serialize()
if !bytes.Equal(data0, data) {
t.Fatalf("program changed after clone/mutate\noriginal:\n%s\n\nnew:\n%s\n", data0, data)
}
data1 := p1.Serialize()
if !bytes.Equal(data, data1) {
continue next
}
}
t.Fatalf("mutation does not change program")
}
}
func TestMutateTable(t *testing.T) {
if testing.Short() {
t.Skip("skipping in short mode")
}
tests := [][2]string{
// Insert calls.
{
"mmap(&(0x7f0000000000)=nil, (0x1000), 0x3, 0x32, 0xffffffffffffffff, 0x0)\n" +
"pipe2(&(0x7f0000000000)={0x0, 0x0}, 0x0)\n",
"mmap(&(0x7f0000000000)=nil, (0x1000), 0x3, 0x32, 0xffffffffffffffff, 0x0)\n" +
"sched_yield()\n" +
"pipe2(&(0x7f0000000000)={0x0, 0x0}, 0x0)\n",
},
// Remove calls and update args.
{
"r0 = open(&(0x7f0000001000)=\"2e2f66696c653000\", 0x22c0, 0x1)\n" +
"sched_yield()\n" +
"read(r0, &(0x7f0000000000)=0x0, 0x1)\n" +
"sched_yield()\n",
"sched_yield()\n" +
"read(0xffffffffffffffff, &(0x7f0000000000)=0x0, 0x1)\n" +
"sched_yield()\n",
},
// Mutate flags.
{
"r0 = open(&(0x7f0000001000)=\"2e2f66696c653000\", 0x22c0, 0x1)\n" +
"sched_yield()\n" +
"read(r0, &(0x7f0000000000)=0x0, 0x1)\n" +
"sched_yield()\n",
"r0 = open(&(0x7f0000001000)=\"2e2f66696c653000\", 0x22c0, 0x2)\n" +
"sched_yield()\n" +
"read(r0, &(0x7f0000000000)=0x0, 0x1)\n" +
"sched_yield()\n",
},
// Mutate data (delete byte and update size).
{
"r0 = open(&(0x7f0000001000)=\"2e2f66696c653000\", 0x22c0, 0x1)\n" +
"write(r0, &(0x7f0000000000)=\"11223344\", 0x4)\n",
"r0 = open(&(0x7f0000001000)=\"2e2f66696c653000\", 0x22c0, 0x1)\n" +
"write(r0, &(0x7f0000000000)=\"112244\", 0x3)\n",
},
// Mutate data (insert byte and update size).
{
"r0 = open(&(0x7f0000001000)=\"2e2f66696c653000\", 0x22c0, 0x1)\n" +
"write(r0, &(0x7f0000000000)=\"1122\", 0x2)\n",
"r0 = open(&(0x7f0000001000)=\"2e2f66696c653000\", 0x22c0, 0x1)\n" +
"write(r0, &(0x7f0000000000)=\"112255\", 0x3)\n",
},
// Mutate data (change byte).
{
"r0 = open(&(0x7f0000001000)=\"2e2f66696c653000\", 0x22c0, 0x1)\n" +
"write(r0, &(0x7f0000000000)=\"1122\", 0x2)\n",
"r0 = open(&(0x7f0000001000)=\"2e2f66696c653000\", 0x22c0, 0x1)\n" +
"write(r0, &(0x7f0000000000)=\"1155\", 0x2)\n",
},
// Change filename.
{
"open(&(0x7f0000001000)=\"2e2f66696c653000\", 0x22c0, 0x1)\n" +
"r0 = open(&(0x7f0000001000)=\"2e2f66696c653000\", 0x22c0, 0x1)\n" +
"write(r0, &(0x7f0000000000)=\"\", 0x0)\n",
"open(&(0x7f0000001000)=\"2e2f66696c653000\", 0x22c0, 0x1)\n" +
"r0 = open(&(0x7f0000001000)=\"2e2f66696c653100\", 0x22c0, 0x1)\n" +
"write(r0, &(0x7f0000000000)=\"\", 0x0)\n",
},
// Extend an array.
{
"r0 = open(&(0x7f0000001000)=\"2e2f66696c653000\", 0x22c0, 0x1)\n" +
"readv(r0, &(0x7f0000000000)={{&(0x7f0000001000)=nil, 0x1}, {&(0x7f0000002000)=nil, 0x2}}, 0x2)\n",
"mmap(&(0x7f0000000000)=nil, (0x1000), 0x3, 0x32, 0xffffffffffffffff, 0x0)\n" +
"r0 = open(&(0x7f0000001000)=\"2e2f66696c653000\", 0x22c0, 0x1)\n" +
"readv(r0, &(0x7f0000000000)={{&(0x7f0000001000)=nil, 0x1}, {&(0x7f0000002000)=nil, 0x2}, {&(0x7f0000000000)=nil, 0x3}}, 0x3)\n",
},
}
rs, _ := initTest(t)
nextTest:
for ti, test := range tests {
p, err := Deserialize([]byte(test[0]))
if err != nil {
t.Fatalf("failed to deserialize original program: %v", err)
}
for i := 0; i < 1e6; i++ {
p1 := p.Clone()
p1.Mutate(rs, 30, nil)
data1 := p1.Serialize()
if string(data1) == test[1] {
t.Logf("test #%v: success on iter %v", ti, i)
continue nextTest
}
_ = fmt.Printf
}
t.Fatalf("failed to achieve mutation goal\noriginal:\n%s\n\ngoal:\n%s\n", test[0], test[1])
}
}
func TestMinimize(t *testing.T) {
tests := []struct {
orig string
callIndex int
pred func(*Prog, int) bool
result string
}{
// Predicate always returns false, so must get the same program.
{
"mmap(&(0x7f0000000000)=nil, (0x1000), 0x3, 0x32, 0xffffffffffffffff, 0x0)\n" +
"sched_yield()\n" +
"pipe2(&(0x7f0000000000)={0x0, 0x0}, 0x0)\n",
2,
func(p *Prog, callIndex int) bool {
if len(p.Calls) == 0 {
t.Fatalf("got an empty program")
}
if p.Calls[len(p.Calls)-1].Meta.Name != "pipe2" {
t.Fatalf("last call is removed")
}
return false
},
"mmap(&(0x7f0000000000)=nil, (0x1000), 0x3, 0x32, 0xffffffffffffffff, 0x0)\n" +
"sched_yield()\n" +
"pipe2(&(0x7f0000000000)={0x0, 0x0}, 0x0)\n",
},
// Remove a call.
{
"mmap(&(0x7f0000000000)=nil, (0x1000), 0x3, 0x32, 0xffffffffffffffff, 0x0)\n" +
"sched_yield()\n" +
"pipe2(&(0x7f0000000000)={0x0, 0x0}, 0x0)\n",
2,
func(p *Prog, callIndex int) bool {
// Aim at removal of sched_yield.
return len(p.Calls) == 2 && p.Calls[0].Meta.Name == "mmap" && p.Calls[1].Meta.Name == "pipe2"
},
"mmap(&(0x7f0000000000)=nil, (0x1000), 0x3, 0x32, 0xffffffffffffffff, 0x0)\n" +
"pipe2(&(0x7f0000000000)={0x0, 0x0}, 0x0)\n",
},
// Remove two dependent calls.
{
"mmap(&(0x7f0000000000)=nil, (0x1000), 0x3, 0x32, 0xffffffffffffffff, 0x0)\n" +
"pipe2(&(0x7f0000000000)={0x0, 0x0}, 0x0)\n" +
"sched_yield()\n",
2,
func(p *Prog, callIndex int) bool {
// Aim at removal of pipe2 and then mmap.
if len(p.Calls) == 2 && p.Calls[0].Meta.Name == "mmap" && p.Calls[1].Meta.Name == "sched_yield" {
return true
}
if len(p.Calls) == 1 && p.Calls[0].Meta.Name == "sched_yield" {
return true
}
return false
},
"sched_yield()\n",
},
// Remove a call and replace results.
{
"mmap(&(0x7f0000000000)=nil, (0x1000), 0x3, 0x32, 0xffffffffffffffff, 0x0)\n" +
"pipe2(&(0x7f0000000000)={[r0=]0x0, 0x0}, 0x0)\n" +
"write(r0, &(0x7f0000000000)=\"1155\", 0x2)\n" +
"sched_yield()\n",
3,
func(p *Prog, callIndex int) bool {
return p.String() == "mmap-write-sched_yield"
},
"mmap(&(0x7f0000000000)=nil, (0x1000), 0x3, 0x32, 0xffffffffffffffff, 0x0)\n" +
"write(0xffffffffffffffff, &(0x7f0000000000)=\"1155\", 0x2)\n" +
"sched_yield()\n",
},
// Remove a call and replace results.
{
"mmap(&(0x7f0000000000)=nil, (0x1000), 0x3, 0x32, 0xffffffffffffffff, 0x0)\n" +
"r0=open(&(0x7f0000000000)=\"1155\", 0x0, 0x0)\n" +
"write(r0, &(0x7f0000000000)=\"1155\", 0x2)\n" +
"sched_yield()\n",
3,
func(p *Prog, callIndex int) bool {
return p.String() == "mmap-write-sched_yield"
},
"mmap(&(0x7f0000000000)=nil, (0x1000), 0x3, 0x32, 0xffffffffffffffff, 0x0)\n" +
"write(0xffffffffffffffff, &(0x7f0000000000)=\"1155\", 0x2)\n" +
"sched_yield()\n",
},
}
for ti, test := range tests {
p, err := Deserialize([]byte(test.orig))
if err != nil {
t.Fatalf("failed to deserialize original program: %v", err)
}
p1, _ := Minimize(p, test.callIndex, test.pred)
res := p1.Serialize()
if string(res) != test.result {
t.Fatalf("minimization produced wrong result #%v\norig:\n%v\nexpect:\n%v\ngot:\n%v\n",
ti, test.orig, test.result, string(res))
}
}
}
func TestMinimizeRandom(t *testing.T) {
rs, iters := initTest(t)
for i := 0; i < iters; i++ {
p := Generate(rs, 10, nil)
Minimize(p, len(p.Calls)-1, func(p1 *Prog, callIndex int) bool {
if err := p1.validate(); err != nil {
t.Fatalf("invalid program: %v", err)
}
return false
})
Minimize(p, len(p.Calls)-1, func(p1 *Prog, callIndex int) bool {
if err := p1.validate(); err != nil {
t.Fatalf("invalid program: %v", err)
}
return true
})
}
}