blob: 75e451b9bba8db310e7e06f2d767a2cda4265de0 [file] [log] [blame]
// 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 csource
import (
"fmt"
"math/rand"
"os"
"os/exec"
"runtime"
"testing"
"time"
"github.com/google/syzkaller/prog"
_ "github.com/google/syzkaller/sys"
"github.com/google/syzkaller/sys/targets"
)
func TestGenerate(t *testing.T) {
t.Parallel()
checked := make(map[string]bool)
for _, target := range prog.AllTargets() {
target := target
sysTarget := targets.Get(target.OS, target.Arch)
if runtime.GOOS != sysTarget.BuildOS {
continue
}
t.Run(target.OS+"/"+target.Arch, func(t *testing.T) {
if target.OS == "linux" && target.Arch == "arm" {
// This currently fails (at least with my arm-linux-gnueabihf-gcc-4.8) with:
// Assembler messages:
// Error: alignment too large: 15 assumed
t.Skip("broken")
}
if target.OS == "linux" && target.Arch == "386" {
// Currently fails on travis with:
// fatal error: asm/unistd.h: No such file or directory
t.Skip("broken")
}
if target.OS == "linux" && target.Arch == "arm64" {
// Episodically fails on travis with:
// collect2: error: ld terminated with signal 11 [Segmentation fault]
t.Skip("broken")
}
if target.OS == "test" && target.PtrSize == 4 {
// The same reason as linux/32.
t.Skip("broken")
}
if _, err := exec.LookPath(sysTarget.CCompiler); err != nil {
t.Skipf("no target compiler %v", sysTarget.CCompiler)
}
full := !checked[target.OS]
checked[target.OS] = true
t.Parallel()
testTarget(t, target, full)
})
}
}
func testTarget(t *testing.T, target *prog.Target, full bool) {
seed := time.Now().UnixNano()
if os.Getenv("TRAVIS") != "" {
seed = 0 // required for deterministic coverage reports
}
rs := rand.NewSource(seed)
t.Logf("seed=%v", seed)
p := target.Generate(rs, 10, nil)
// Turns out that fully minimized program can trigger new interesting warnings,
// e.g. about NULL arguments for functions that require non-NULL arguments in syz_ functions.
// We could append both AllSyzProg as-is and a minimized version of it,
// but this makes the NULL argument warnings go away (they showed up in ".constprop" versions).
// Testing 2 programs takes too long since we have lots of options permutations and OS/arch.
// So we use the as-is in short tests and minimized version in full tests.
syzProg := target.GenerateAllSyzProg(rs)
var opts []Options
if !full || testing.Short() {
p.Calls = append(p.Calls, syzProg.Calls...)
opts = allOptionsSingle(target.OS)
// This is the main configuration used by executor,
// so we want to test it as well.
opts = append(opts, Options{
Threaded: true,
Collide: true,
Repeat: true,
Procs: 2,
Sandbox: "none",
Repro: true,
UseTmpDir: true,
})
} else {
minimized, _ := prog.Minimize(syzProg, -1, false, func(p *prog.Prog, call int) bool {
return len(p.Calls) == len(syzProg.Calls)
})
p.Calls = append(p.Calls, minimized.Calls...)
opts = allOptionsPermutations(target.OS)
}
for opti, opts := range opts {
opts := opts
t.Run(fmt.Sprintf("%v", opti), func(t *testing.T) {
t.Parallel()
testOne(t, p, opts)
})
}
}
func testOne(t *testing.T, p *prog.Prog, opts Options) {
src, err := Write(p, opts)
if err != nil {
t.Logf("opts: %+v\nprogram:\n%s\n", opts, p.Serialize())
t.Fatalf("%v", err)
}
bin, err := Build(p.Target, src)
if err != nil {
t.Logf("opts: %+v\nprogram:\n%s\n", opts, p.Serialize())
t.Fatalf("%v", err)
}
defer os.Remove(bin)
}