aboutsummaryrefslogtreecommitdiffstats
path: root/pkg
diff options
context:
space:
mode:
authorAleksandr Nogikh <nogikh@google.com>2021-09-23 16:15:41 +0000
committerAleksandr Nogikh <wp32pw@gmail.com>2021-12-10 12:30:07 +0100
commitfd8caa5462e64f37cb9eebd75ffca1737dde447d (patch)
treebfa900ebf41099b21476e72acdf063ee630178c9 /pkg
parent4d4ce9bc2a12073dcc8b917f9fc2a4ecba26c4c5 (diff)
all: replace collide mode by `async` call property
Replace the currently existing straightforward approach to race triggering (that was almost entirely implemented inside syz-executor) with a more flexible one. The `async` call property instructs syz-executor not to block until the call has completed execution and proceed immediately to the next call. The decision on what calls to mark with `async` is made by syz-fuzzer. Ultimately this should let us implement more intelligent race provoking strategies as well as make more fine-grained reproducers.
Diffstat (limited to 'pkg')
-rw-r--r--pkg/csource/common.go10
-rw-r--r--pkg/csource/csource.go11
-rw-r--r--pkg/csource/csource_test.go5
-rw-r--r--pkg/csource/generated.go14
-rw-r--r--pkg/csource/options.go4
-rw-r--r--pkg/csource/options_test.go10
-rw-r--r--pkg/instance/instance_test.go11
-rw-r--r--pkg/ipc/ipc.go4
-rw-r--r--pkg/ipc/ipc_test.go2
-rw-r--r--pkg/ipc/ipcconfig/ipcconfig.go5
-rw-r--r--pkg/repro/repro.go9
-rw-r--r--pkg/repro/repro_test.go1
-rw-r--r--pkg/runtest/run.go5
13 files changed, 43 insertions, 48 deletions
diff --git a/pkg/csource/common.go b/pkg/csource/common.go
index a1525e24f..7e396931c 100644
--- a/pkg/csource/common.go
+++ b/pkg/csource/common.go
@@ -91,23 +91,23 @@ func defineList(p, mmapProg *prog.Prog, opts Options) (defines []string) {
func commonDefines(p *prog.Prog, opts Options) map[string]bool {
sysTarget := targets.Get(p.Target.OS, p.Target.Arch)
- bitmasks, csums := prog.RequiredFeatures(p)
+ features := p.RequiredFeatures()
return map[string]bool{
"GOOS_" + p.Target.OS: true,
"GOARCH_" + p.Target.Arch: true,
"HOSTGOOS_" + runtime.GOOS: true,
- "SYZ_USE_BITMASKS": bitmasks,
- "SYZ_USE_CHECKSUMS": csums,
+ "SYZ_USE_BITMASKS": features.Bitmasks,
+ "SYZ_USE_CHECKSUMS": features.Csums,
"SYZ_SANDBOX_NONE": opts.Sandbox == sandboxNone,
"SYZ_SANDBOX_SETUID": opts.Sandbox == sandboxSetuid,
"SYZ_SANDBOX_NAMESPACE": opts.Sandbox == sandboxNamespace,
"SYZ_SANDBOX_ANDROID": opts.Sandbox == sandboxAndroid,
"SYZ_THREADED": opts.Threaded,
- "SYZ_COLLIDE": opts.Collide,
+ "SYZ_ASYNC": features.Async,
"SYZ_REPEAT": opts.Repeat,
"SYZ_REPEAT_TIMES": opts.RepeatTimes > 1,
"SYZ_MULTI_PROC": opts.Procs > 1,
- "SYZ_FAULT": p.HasFaultInjection(),
+ "SYZ_FAULT": features.FaultInjection,
"SYZ_LEAK": opts.Leak,
"SYZ_NET_INJECTION": opts.NetInjection,
"SYZ_NET_DEVICES": opts.NetDevices,
diff --git a/pkg/csource/csource.go b/pkg/csource/csource.go
index 68f961526..32e01ff1e 100644
--- a/pkg/csource/csource.go
+++ b/pkg/csource/csource.go
@@ -115,6 +115,17 @@ func (ctx *context) generateSource() ([]byte, error) {
}
}
replacements["CALL_TIMEOUT_MS"] = timeoutExpr
+ if ctx.p.RequiredFeatures().Async {
+ conditions := []string{}
+ for idx, call := range ctx.p.Calls {
+ if !call.Props.Async {
+ continue
+ }
+ conditions = append(conditions, fmt.Sprintf("call == %v", idx))
+ }
+ replacements["ASYNC_CONDITIONS"] = strings.Join(conditions, " || ")
+ }
+
result, err := createCommonHeader(ctx.p, mmapProg, replacements, ctx.opts)
if err != nil {
return nil, err
diff --git a/pkg/csource/csource_test.go b/pkg/csource/csource_test.go
index aa242e876..885d75f67 100644
--- a/pkg/csource/csource_test.go
+++ b/pkg/csource/csource_test.go
@@ -70,10 +70,13 @@ func testTarget(t *testing.T, target *prog.Target, full bool) {
p.Calls = append(p.Calls, minimized.Calls...)
opts = allOptionsPermutations(target.OS)
}
+ // Test fault injection and async call generation as well.
if len(p.Calls) > 0 {
- // Test fault injection code generation as well.
p.Calls[0].Props.FailNth = 1
}
+ if len(p.Calls) > 1 {
+ p.Calls[1].Props.Async = true
+ }
for opti, opts := range opts {
if testing.Short() && opts.HandleSegv {
// HandleSegv can radically increase compilation time/memory consumption on large programs.
diff --git a/pkg/csource/generated.go b/pkg/csource/generated.go
index 728806967..851bf935a 100644
--- a/pkg/csource/generated.go
+++ b/pkg/csource/generated.go
@@ -10457,10 +10457,6 @@ static void loop(void)
fprintf(stderr, "### start\n");
#endif
int i, call, thread;
-#if SYZ_COLLIDE
- int collide = 0;
-again:
-#endif
for (call = 0; call < /*{{{NUM_CALLS}}}*/; call++) {
for (thread = 0; thread < (int)(sizeof(threads) / sizeof(threads[0])); thread++) {
struct thread_t* th = &threads[thread];
@@ -10477,8 +10473,8 @@ again:
th->call = call;
__atomic_fetch_add(&running, 1, __ATOMIC_RELAXED);
event_set(&th->ready);
-#if SYZ_COLLIDE
- if (collide && (call % 2) == 0)
+#if SYZ_ASYNC
+ if (/*{{{ASYNC_CONDITIONS}}}*/)
break;
#endif
event_timedwait(&th->done, /*{{{CALL_TIMEOUT_MS}}}*/);
@@ -10490,12 +10486,6 @@ again:
#if SYZ_HAVE_CLOSE_FDS
close_fds();
#endif
-#if SYZ_COLLIDE
- if (!collide) {
- collide = 1;
- goto again;
- }
-#endif
}
#endif
diff --git a/pkg/csource/options.go b/pkg/csource/options.go
index 36490c8b8..3fc549282 100644
--- a/pkg/csource/options.go
+++ b/pkg/csource/options.go
@@ -19,7 +19,6 @@ import (
// Dashboard also provides serialized Options along with syzkaller reproducers.
type Options struct {
Threaded bool `json:"threaded,omitempty"`
- Collide bool `json:"collide,omitempty"`
Repeat bool `json:"repeat,omitempty"`
RepeatTimes int `json:"repeat_times,omitempty"` // if non-0, repeat that many times
Procs int `json:"procs"`
@@ -55,6 +54,7 @@ type Options struct {
// These are legacy options, they remain only for the sake of backward compatibility.
type LegacyOptions struct {
+ Collide bool `json:"collide,omitempty"`
Fault bool `json:"fault,omitempty"`
FaultCall int `json:"fault_call,omitempty"`
FaultNth int `json:"fault_nth,omitempty"`
@@ -158,7 +158,6 @@ func (opts Options) checkLinuxOnly(OS string) error {
func DefaultOpts(cfg *mgrconfig.Config) Options {
opts := Options{
Threaded: true,
- Collide: true,
Repeat: true,
Procs: cfg.Procs,
Slowdown: cfg.Timeouts.Slowdown,
@@ -322,7 +321,6 @@ func PrintAvailableFeaturesFlags() {
// This is the main configuration used by executor, only for testing.
var ExecutorOpts = Options{
Threaded: true,
- Collide: true,
Repeat: true,
Procs: 2,
Slowdown: 1,
diff --git a/pkg/csource/options_test.go b/pkg/csource/options_test.go
index ba31e4c95..5e971a9d1 100644
--- a/pkg/csource/options_test.go
+++ b/pkg/csource/options_test.go
@@ -34,7 +34,6 @@ func TestParseOptionsCanned(t *testing.T) {
"netdev":true,"resetnet":true,
"segv":true,"waitrepeat":true,"debug":true,"repro":true}`: {
Threaded: true,
- Collide: true,
Repeat: true,
Procs: 10,
Slowdown: 1,
@@ -49,6 +48,7 @@ func TestParseOptionsCanned(t *testing.T) {
HandleSegv: true,
Repro: true,
LegacyOptions: LegacyOptions{
+ Collide: true,
Fault: true,
FaultCall: 1,
FaultNth: 2,
@@ -59,7 +59,6 @@ func TestParseOptionsCanned(t *testing.T) {
"netdev":true,"resetnet":true,
"segv":true,"waitrepeat":true,"debug":true,"repro":true}`: {
Threaded: true,
- Collide: true,
Repeat: true,
Procs: 10,
Slowdown: 1,
@@ -74,6 +73,7 @@ func TestParseOptionsCanned(t *testing.T) {
HandleSegv: true,
Repro: true,
LegacyOptions: LegacyOptions{
+ Collide: true,
Fault: true,
FaultCall: 1,
FaultNth: 2,
@@ -81,7 +81,6 @@ func TestParseOptionsCanned(t *testing.T) {
},
"{Threaded:true Collide:true Repeat:true Procs:1 Sandbox:none Fault:false FaultCall:-1 FaultNth:0 EnableTun:true UseTmpDir:true HandleSegv:true WaitRepeat:true Debug:false Repro:false}": {
Threaded: true,
- Collide: true,
Repeat: true,
Procs: 1,
Slowdown: 1,
@@ -94,6 +93,7 @@ func TestParseOptionsCanned(t *testing.T) {
HandleSegv: true,
Repro: false,
LegacyOptions: LegacyOptions{
+ Collide: true,
Fault: false,
FaultCall: -1,
FaultNth: 0,
@@ -101,7 +101,6 @@ func TestParseOptionsCanned(t *testing.T) {
},
"{Threaded:true Collide:true Repeat:true Procs:1 Sandbox: Fault:false FaultCall:-1 FaultNth:0 EnableTun:true UseTmpDir:true HandleSegv:true WaitRepeat:true Debug:false Repro:false}": {
Threaded: true,
- Collide: true,
Repeat: true,
Procs: 1,
Slowdown: 1,
@@ -114,6 +113,7 @@ func TestParseOptionsCanned(t *testing.T) {
HandleSegv: true,
Repro: false,
LegacyOptions: LegacyOptions{
+ Collide: true,
Fault: false,
FaultCall: -1,
FaultNth: 0,
@@ -121,7 +121,6 @@ func TestParseOptionsCanned(t *testing.T) {
},
"{Threaded:false Collide:true Repeat:true Procs:1 Sandbox:namespace Fault:false FaultCall:-1 FaultNth:0 EnableTun:true UseTmpDir:true EnableCgroups:true HandleSegv:true WaitRepeat:true Debug:false Repro:false}": {
Threaded: false,
- Collide: true,
Repeat: true,
Procs: 1,
Slowdown: 1,
@@ -134,6 +133,7 @@ func TestParseOptionsCanned(t *testing.T) {
HandleSegv: true,
Repro: false,
LegacyOptions: LegacyOptions{
+ Collide: true,
Fault: false,
FaultCall: -1,
FaultNth: 0,
diff --git a/pkg/instance/instance_test.go b/pkg/instance/instance_test.go
index 5b0bcd4e6..e27361091 100644
--- a/pkg/instance/instance_test.go
+++ b/pkg/instance/instance_test.go
@@ -91,12 +91,15 @@ func TestExecprogCmd(t *testing.T) {
flagFaultNth := flags.Int("fault_nth", 0, "inject fault on n-th operation (0-based)")
flagExecutor := flags.String("executor", "./syz-executor", "path to executor binary")
flagThreaded := flags.Bool("threaded", true, "use threaded mode in executor")
- flagCollide := flags.Bool("collide", true, "collide syscalls to provoke data races")
+ // In the older syzkaller versions `collide` flag defaulted to `true`, but in this
+ // test we can change it to false (new default), because syzkaller always explicitly
+ // sets this flag and never relies on the default value.
+ flagCollide := flags.Bool("collide", false, "collide syscalls to provoke data races")
flagSignal := flags.Bool("cover", false, "collect feedback signals (coverage)")
flagSandbox := flags.String("sandbox", "none", "sandbox for fuzzing (none/setuid/namespace)")
flagSlowdown := flags.Int("slowdown", 1, "")
cmdLine := ExecprogCmd(os.Args[0], "/myexecutor", targets.FreeBSD, targets.I386,
- "namespace", true, false, false, 7, 2, 3, true, 10, "myprog")
+ "namespace", true, false, true, 7, 2, 3, true, 10, "myprog")
args := strings.Split(cmdLine, " ")[1:]
if err := tool.ParseFlags(flags, args); err != nil {
t.Fatal(err)
@@ -134,8 +137,8 @@ func TestExecprogCmd(t *testing.T) {
if *flagThreaded {
t.Errorf("bad threaded: %v, want: %v", *flagThreaded, false)
}
- if *flagCollide {
- t.Errorf("bad collide: %v, want: %v", *flagCollide, false)
+ if !*flagCollide {
+ t.Errorf("bad collide: %v, want: %v", *flagCollide, true)
}
if *flagSlowdown != 10 {
t.Errorf("bad slowdown: %v, want: %v", *flagSlowdown, 10)
diff --git a/pkg/ipc/ipc.go b/pkg/ipc/ipc.go
index 03b28e4ce..5bf4738ca 100644
--- a/pkg/ipc/ipc.go
+++ b/pkg/ipc/ipc.go
@@ -49,11 +49,11 @@ const (
type ExecFlags uint64
const (
- FlagCollectCover ExecFlags = 1 << iota // collect coverage
+ FlagCollectSignal ExecFlags = 1 << iota // collect feedback signals
+ FlagCollectCover // collect coverage
FlagDedupCover // deduplicate coverage in executor
FlagCollectComps // collect KCOV comparisons
FlagThreaded // use multiple threads to mitigate blocked syscalls
- FlagCollide // collide syscalls to provoke data races
FlagEnableCoverageFilter // setup and use bitmap to do coverage filter
)
diff --git a/pkg/ipc/ipc_test.go b/pkg/ipc/ipc_test.go
index 44fdb67bd..bb110e388 100644
--- a/pkg/ipc/ipc_test.go
+++ b/pkg/ipc/ipc_test.go
@@ -87,7 +87,7 @@ func TestExecute(t *testing.T) {
bin := buildExecutor(t, target)
defer os.Remove(bin)
- flags := []ExecFlags{0, FlagThreaded, FlagThreaded | FlagCollide}
+ flags := []ExecFlags{0, FlagThreaded}
for _, flag := range flags {
t.Logf("testing flags 0x%x\n", flag)
cfg := &Config{
diff --git a/pkg/ipc/ipcconfig/ipcconfig.go b/pkg/ipc/ipcconfig/ipcconfig.go
index 3791322f2..5be4d4b39 100644
--- a/pkg/ipc/ipcconfig/ipcconfig.go
+++ b/pkg/ipc/ipcconfig/ipcconfig.go
@@ -14,7 +14,6 @@ import (
var (
flagExecutor = flag.String("executor", "./syz-executor", "path to executor binary")
flagThreaded = flag.Bool("threaded", true, "use threaded mode in executor")
- flagCollide = flag.Bool("collide", true, "collide syscalls to provoke data races")
flagSignal = flag.Bool("cover", false, "collect feedback signals (coverage)")
flagSandbox = flag.String("sandbox", "none", "sandbox for fuzzing (none/setuid/namespace/android)")
flagDebug = flag.Bool("debug", false, "debug output from executor")
@@ -46,8 +45,8 @@ func Default(target *prog.Target) (*ipc.Config, *ipc.ExecOpts, error) {
if *flagThreaded {
opts.Flags |= ipc.FlagThreaded
}
- if *flagCollide {
- opts.Flags |= ipc.FlagCollide
+ if *flagSignal {
+ opts.Flags |= ipc.FlagCollectSignal
}
return c, opts, nil
diff --git a/pkg/repro/repro.go b/pkg/repro/repro.go
index 8eebb5c6d..06afec8c2 100644
--- a/pkg/repro/repro.go
+++ b/pkg/repro/repro.go
@@ -442,7 +442,7 @@ func (ctx *context) minimizeProg(res *Result) (*Result, error) {
return res, nil
}
-// Simplify repro options (threaded, collide, sandbox, etc).
+// Simplify repro options (threaded, sandbox, etc).
func (ctx *context) simplifyProg(res *Result) (*Result, error) {
ctx.reproLogf(2, "simplifying guilty program options")
start := time.Now()
@@ -784,13 +784,6 @@ type Simplify func(opts *csource.Options) bool
var progSimplifies = []Simplify{
func(opts *csource.Options) bool {
- if !opts.Collide {
- return false
- }
- opts.Collide = false
- return true
- },
- func(opts *csource.Options) bool {
if opts.Collide || !opts.Threaded {
return false
}
diff --git a/pkg/repro/repro_test.go b/pkg/repro/repro_test.go
index 9b639cd7d..d02ea6d67 100644
--- a/pkg/repro/repro_test.go
+++ b/pkg/repro/repro_test.go
@@ -79,7 +79,6 @@ func TestBisect(t *testing.T) {
func TestSimplifies(t *testing.T) {
opts := csource.Options{
Threaded: true,
- Collide: true,
Repeat: true,
Procs: 10,
Sandbox: "namespace",
diff --git a/pkg/runtest/run.go b/pkg/runtest/run.go
index 1d3412cb0..87634a3dc 100644
--- a/pkg/runtest/run.go
+++ b/pkg/runtest/run.go
@@ -403,7 +403,7 @@ func (ctx *Context) createSyzTest(p *prog.Prog, sandbox string, threaded, cov bo
}
cfg.Flags |= sandboxFlags
if threaded {
- opts.Flags |= ipc.FlagThreaded | ipc.FlagCollide
+ opts.Flags |= ipc.FlagThreaded
}
if cov {
cfg.Flags |= ipc.FlagSignal
@@ -447,7 +447,6 @@ func (ctx *Context) createSyzTest(p *prog.Prog, sandbox string, threaded, cov bo
func (ctx *Context) createCTest(p *prog.Prog, sandbox string, threaded bool, times int) (*RunRequest, error) {
opts := csource.Options{
Threaded: threaded,
- Collide: false,
Repeat: times > 1,
RepeatTimes: times,
Procs: 1,
@@ -485,7 +484,7 @@ func (ctx *Context) createCTest(p *prog.Prog, sandbox string, threaded bool, tim
}
var ipcFlags ipc.ExecFlags
if threaded {
- ipcFlags |= ipc.FlagThreaded | ipc.FlagCollide
+ ipcFlags |= ipc.FlagThreaded
}
req := &RunRequest{
P: p,