1// Copyright 2015 syzkaller project authors. All rights reserved.
2// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
3
4package ipc_test
5
6import (
7	"fmt"
8	"math/rand"
9	"os"
10	"path/filepath"
11	"runtime"
12	"testing"
13	"time"
14
15	"github.com/google/syzkaller/pkg/csource"
16	. "github.com/google/syzkaller/pkg/ipc"
17	"github.com/google/syzkaller/pkg/ipc/ipcconfig"
18	"github.com/google/syzkaller/pkg/osutil"
19	"github.com/google/syzkaller/prog"
20	_ "github.com/google/syzkaller/sys"
21)
22
23const timeout = 10 * time.Second
24
25func buildExecutor(t *testing.T, target *prog.Target) string {
26	src := filepath.FromSlash("../../executor/executor.cc")
27	bin, err := csource.BuildFile(target, src)
28	if err != nil {
29		t.Fatal(err)
30	}
31	return bin
32}
33
34func initTest(t *testing.T) (*prog.Target, rand.Source, int, EnvFlags) {
35	t.Parallel()
36	iters := 100
37	if testing.Short() {
38		iters = 10
39	}
40	seed := int64(time.Now().UnixNano())
41	rs := rand.NewSource(seed)
42	t.Logf("seed=%v", seed)
43	target, err := prog.GetTarget(runtime.GOOS, runtime.GOARCH)
44	if err != nil {
45		t.Fatal(err)
46	}
47	cfg, _, err := ipcconfig.Default(target)
48	if err != nil {
49		t.Fatal(err)
50	}
51	flags := cfg.Flags & (FlagUseShmem | FlagUseForkServer)
52	return target, rs, iters, flags
53}
54
55// TestExecutor runs all internal executor unit tests.
56// We do it here because we already build executor binary here.
57func TestExecutor(t *testing.T) {
58	target, err := prog.GetTarget(runtime.GOOS, runtime.GOARCH)
59	if err != nil {
60		t.Fatal(err)
61	}
62	bin := buildExecutor(t, target)
63	defer os.Remove(bin)
64	output, err := osutil.RunCmd(time.Minute, "", bin, "test")
65	if err != nil {
66		t.Fatal(err)
67	}
68	t.Logf("executor output:\n%s", output)
69}
70
71func TestExecute(t *testing.T) {
72	target, _, _, configFlags := initTest(t)
73
74	bin := buildExecutor(t, target)
75	defer os.Remove(bin)
76
77	flags := []ExecFlags{0, FlagThreaded, FlagThreaded | FlagCollide}
78	for _, flag := range flags {
79		t.Logf("testing flags 0x%x\n", flag)
80		cfg := &Config{
81			Executor: bin,
82			Flags:    configFlags,
83			Timeout:  timeout,
84		}
85		env, err := MakeEnv(cfg, 0)
86		if err != nil {
87			t.Fatalf("failed to create env: %v", err)
88		}
89		defer env.Close()
90
91		for i := 0; i < 10; i++ {
92			p := target.GenerateSimpleProg()
93			opts := &ExecOpts{
94				Flags: flag,
95			}
96			output, info, failed, hanged, err := env.Exec(opts, p)
97			if err != nil {
98				t.Fatalf("failed to run executor: %v", err)
99			}
100			if hanged {
101				t.Fatalf("program hanged:\n%s", output)
102			}
103			if failed {
104				t.Fatalf("program failed:\n%s", output)
105			}
106			if len(info) == 0 {
107				t.Fatalf("no calls executed:\n%s", output)
108			}
109			if info[0].Errno != 0 {
110				t.Fatalf("simple call failed: %v\n%s", info[0].Errno, output)
111			}
112			if len(output) != 0 {
113				t.Fatalf("output on empty program")
114			}
115		}
116	}
117}
118
119func TestParallel(t *testing.T) {
120	target, _, _, configFlags := initTest(t)
121	bin := buildExecutor(t, target)
122	defer os.Remove(bin)
123	cfg := &Config{
124		Executor: bin,
125		Flags:    configFlags,
126	}
127	const P = 10
128	errs := make(chan error, P)
129	for p := 0; p < P; p++ {
130		go func() {
131			env, err := MakeEnv(cfg, 0)
132			if err != nil {
133				errs <- fmt.Errorf("failed to create env: %v", err)
134				return
135			}
136			defer func() {
137				env.Close()
138				errs <- err
139			}()
140			p := target.GenerateSimpleProg()
141			opts := &ExecOpts{}
142			output, info, failed, hanged, err := env.Exec(opts, p)
143			if err != nil {
144				err = fmt.Errorf("failed to run executor: %v", err)
145				return
146			}
147			if hanged {
148				err = fmt.Errorf("program hanged:\n%s", output)
149				return
150			}
151			if failed {
152				err = fmt.Errorf("program failed:\n%s", output)
153				return
154			}
155			if len(info) == 0 {
156				err = fmt.Errorf("no calls executed:\n%s", output)
157				return
158			}
159			if info[0].Errno != 0 {
160				err = fmt.Errorf("simple call failed: %v\n%s", info[0].Errno, output)
161				return
162			}
163			if len(output) != 0 {
164				err = fmt.Errorf("output on empty program")
165				return
166			}
167		}()
168	}
169	for p := 0; p < P; p++ {
170		if err := <-errs; err != nil {
171			t.Fatal(err)
172		}
173	}
174}
175