aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2024-05-06 10:38:20 +0200
committerDmitry Vyukov <dvyukov@google.com>2024-05-06 10:19:03 +0000
commitdc18fdf9b0a2dc9a54167eaf7600075da09d023e (patch)
treef5f1d07ccfdf6d39423f44a86531f930fe4f4775
parentd884b519ef74f7edf51f2c964162f0a2fe80846c (diff)
tools/syz-stress: delete utility
Move syz-stress logic into syz-execprog. It's already doing most of what syz-stress could do, it even can load a corpus since recently. There are few remaining bits that are missing in execprog, so add them to execprog.
-rw-r--r--Makefile7
-rw-r--r--docs/windows/README.md4
-rw-r--r--tools/syz-execprog/execprog.go130
-rw-r--r--tools/syz-stress/stress.go182
4 files changed, 102 insertions, 221 deletions
diff --git a/Makefile b/Makefile
index 584336822..459037457 100644
--- a/Makefile
+++ b/Makefile
@@ -100,7 +100,7 @@ endif
.PHONY: all clean host target \
manager runtest fuzzer executor \
ci hub \
- execprog mutate prog2c trace2syz stress repro upgrade db \
+ execprog mutate prog2c trace2syz repro upgrade db \
usbgen symbolize cover kconf syz-build crush \
bin/syz-extract bin/syz-fmt \
extract generate generate_go generate_rpc generate_sys \
@@ -113,7 +113,7 @@ endif
all: host target
host: manager runtest repro mutate prog2c db upgrade
-target: fuzzer execprog stress executor
+target: fuzzer execprog executor
executor: descriptions
ifeq ($(TARGETOS),fuchsia)
@@ -185,9 +185,6 @@ crush: descriptions
reporter: descriptions
GOOS=$(HOSTOS) GOARCH=$(HOSTARCH) $(HOSTGO) build $(GOHOSTFLAGS) -o ./bin/syz-reporter github.com/google/syzkaller/tools/syz-reporter
-stress: descriptions
- GOOS=$(TARGETGOOS) GOARCH=$(TARGETGOARCH) $(GO) build $(GOTARGETFLAGS) -o ./bin/$(TARGETOS)_$(TARGETVMARCH)/syz-stress$(EXE) github.com/google/syzkaller/tools/syz-stress
-
db: descriptions
GOOS=$(HOSTOS) GOARCH=$(HOSTARCH) $(HOSTGO) build $(GOHOSTFLAGS) -o ./bin/syz-db github.com/google/syzkaller/tools/syz-db
diff --git a/docs/windows/README.md b/docs/windows/README.md
index a511ba86b..a72cec627 100644
--- a/docs/windows/README.md
+++ b/docs/windows/README.md
@@ -35,9 +35,9 @@ cl executor\executor_windows.cc /EHsc -o bin\windows_amd64\syz-executor.exe \
Msimg32.lib RpcRT4.lib Rpcrt4.lib lz32.lib
```
-To run `syz-stress`:
+To run `syz-execprog`:
```
-bin\windows_amd64\syz-stress.exe -executor c:\full\path\to\bin\windows_amd64\syz-executor.exe
+bin\windows_amd64\syz-execprog.exe -executor c:\full\path\to\bin\windows_amd64\syz-executor.exe -stress
```
Windows is supported by only `gce` VMs at the moment.
diff --git a/tools/syz-execprog/execprog.go b/tools/syz-execprog/execprog.go
index 9cfebf15b..6c3314721 100644
--- a/tools/syz-execprog/execprog.go
+++ b/tools/syz-execprog/execprog.go
@@ -9,9 +9,11 @@ import (
"bytes"
"flag"
"fmt"
+ "math/rand"
"os"
"runtime"
"strconv"
+ "strings"
"sync"
"time"
@@ -23,6 +25,7 @@ import (
"github.com/google/syzkaller/pkg/ipc"
"github.com/google/syzkaller/pkg/ipc/ipcconfig"
"github.com/google/syzkaller/pkg/log"
+ "github.com/google/syzkaller/pkg/mgrconfig"
"github.com/google/syzkaller/pkg/osutil"
"github.com/google/syzkaller/pkg/tool"
"github.com/google/syzkaller/prog"
@@ -40,6 +43,17 @@ var (
flagHints = flag.Bool("hints", false, "do a hints-generation run")
flagEnable = flag.String("enable", "none", "enable only listed additional features")
flagDisable = flag.String("disable", "none", "enable all additional features except listed")
+
+ // The in the stress mode resembles simple unguided fuzzer.
+ // This mode can be used as an intermediate step when porting syzkaller to a new OS,
+ // or when testing on a machine that is not supported by the vm package (as syz-manager cannot be used).
+ // To use this mode one needs to start a VM manually, copy syz-execprog and run it.
+ // syz-execprog will execute random programs infinitely until it's stopped or it crashes
+ // the kernel underneath. If it's given a corpus of programs, it will alternate between
+ // executing random programs and mutated programs from the corpus.
+ flagStress = flag.Bool("stress", false, "enable stress mode (local fuzzer)")
+ flagSyscalls = flag.String("syscalls", "", "comma-separated list of enabled syscalls for the stress mode")
+
// The following flag is only kept to let syzkaller remain compatible with older execprog versions.
// In order to test incoming patches or perform bug bisection, syz-ci must use the exact syzkaller
// version that detected the bug (as descriptions and syntax could've already been changed), and
@@ -62,10 +76,6 @@ func main() {
csource.PrintAvailableFeaturesFlags()
}
defer tool.Init()()
- if len(flag.Args()) == 0 {
- flag.Usage()
- os.Exit(1)
- }
featuresFlags, err := csource.ParseFeaturesFlags(*flagEnable, *flagDisable, true)
if err != nil {
log.Fatalf("%v", err)
@@ -77,8 +87,9 @@ func main() {
}
progs := loadPrograms(target, flag.Args())
- if len(progs) == 0 {
- return
+ if !*flagStress && len(progs) == 0 {
+ flag.Usage()
+ os.Exit(1)
}
features, err := host.Check(target)
if err != nil {
@@ -106,17 +117,29 @@ func main() {
}
}
}
+ var choiceTable *prog.ChoiceTable
+ if *flagStress {
+ var syscalls []string
+ if *flagSyscalls != "" {
+ syscalls = strings.Split(*flagSyscalls, ",")
+ }
+ calls := buildCallList(target, syscalls)
+ choiceTable = target.BuildChoiceTable(progs, calls)
+ }
sysTarget := targets.Get(*flagOS, *flagArch)
upperBase := getKernelUpperBase(sysTarget)
ctx := &Context{
- progs: progs,
- config: config,
- execOpts: execOpts,
- gate: ipc.NewGate(2**flagProcs, gateCallback),
- shutdown: make(chan struct{}),
- repeat: *flagRepeat,
- target: sysTarget,
- upperBase: upperBase,
+ target: target,
+ progs: progs,
+ choiceTable: choiceTable,
+ config: config,
+ execOpts: execOpts,
+ gate: ipc.NewGate(2**flagProcs, gateCallback),
+ shutdown: make(chan struct{}),
+ stress: *flagStress,
+ repeat: *flagRepeat,
+ sysTarget: sysTarget,
+ upperBase: upperBase,
}
var wg sync.WaitGroup
wg.Add(*flagProcs)
@@ -132,18 +155,21 @@ func main() {
}
type Context struct {
- progs []*prog.Prog
- config *ipc.Config
- execOpts *ipc.ExecOpts
- gate *ipc.Gate
- shutdown chan struct{}
- logMu sync.Mutex
- posMu sync.Mutex
- repeat int
- pos int
- lastPrint time.Time
- target *targets.Target
- upperBase uint32
+ target *prog.Target
+ progs []*prog.Prog
+ choiceTable *prog.ChoiceTable
+ config *ipc.Config
+ execOpts *ipc.ExecOpts
+ gate *ipc.Gate
+ shutdown chan struct{}
+ logMu sync.Mutex
+ posMu sync.Mutex
+ stress bool
+ repeat int
+ pos int
+ lastPrint time.Time
+ sysTarget *targets.Target
+ upperBase uint32
}
func (ctx *Context) run(pid int) {
@@ -152,18 +178,24 @@ func (ctx *Context) run(pid int) {
log.Fatalf("failed to create ipc env: %v", err)
}
defer env.Close()
+ rs := rand.NewSource(time.Now().UnixNano() + int64(pid)*1e12)
for {
select {
case <-ctx.shutdown:
return
default:
}
- idx := ctx.getProgramIndex()
- if ctx.repeat > 0 && idx >= len(ctx.progs)*ctx.repeat {
- return
+ if ctx.stress {
+ p := ctx.createStressProg(rs)
+ ctx.execute(pid, env, p, 0)
+ } else {
+ idx := ctx.getProgramIndex()
+ if ctx.repeat > 0 && idx >= len(ctx.progs)*ctx.repeat {
+ return
+ }
+ p := ctx.progs[idx%len(ctx.progs)]
+ ctx.execute(pid, env, p, idx)
}
- entry := ctx.progs[idx%len(ctx.progs)]
- ctx.execute(pid, env, entry, idx)
}
}
@@ -301,7 +333,8 @@ func (ctx *Context) dumpCallCoverage(coverFile string, info *ipc.CallInfo) {
}
buf := new(bytes.Buffer)
for _, pc := range info.Cover {
- fmt.Fprintf(buf, "0x%x\n", backend.PreviousInstructionPC(ctx.target, cover.RestorePC(pc, ctx.upperBase)))
+ prev := backend.PreviousInstructionPC(ctx.sysTarget, cover.RestorePC(pc, ctx.upperBase))
+ fmt.Fprintf(buf, "0x%x\n", prev)
}
err := osutil.WriteFile(coverFile, buf.Bytes())
if err != nil {
@@ -330,6 +363,16 @@ func (ctx *Context) getProgramIndex() int {
return idx
}
+func (ctx *Context) createStressProg(rs rand.Source) *prog.Prog {
+ rnd := rand.New(rs)
+ if len(ctx.progs) == 0 || rnd.Intn(2) == 0 {
+ return ctx.target.Generate(rs, prog.RecommendedCalls, ctx.choiceTable)
+ }
+ p := ctx.progs[rnd.Intn(len(ctx.progs))].Clone()
+ p.Mutate(rs, prog.RecommendedCalls, ctx.choiceTable, nil, ctx.progs)
+ return p
+}
+
func loadPrograms(target *prog.Target, files []string) []*prog.Prog {
var progs []*prog.Prog
for _, fn := range files {
@@ -378,3 +421,26 @@ func createConfig(target *prog.Target, features *host.Features, featuresFlags cs
execOpts.EnvFlags |= ipc.FeaturesToFlags(features, featuresFlags)
return config, execOpts
}
+
+func buildCallList(target *prog.Target, enabled []string) map[*prog.Syscall]bool {
+ syscallsIDs, err := mgrconfig.ParseEnabledSyscalls(target, enabled, nil)
+ if err != nil {
+ log.Fatalf("failed to parse enabled syscalls: %v", err)
+ }
+ enabledSyscalls := make(map[*prog.Syscall]bool)
+ for _, id := range syscallsIDs {
+ enabledSyscalls[target.Syscalls[id]] = true
+ }
+ calls, disabled, err := host.DetectSupportedSyscalls(target, "none", enabledSyscalls)
+ if err != nil {
+ log.Fatalf("failed to detect host supported syscalls: %v", err)
+ }
+ for c, reason := range disabled {
+ log.Logf(0, "unsupported syscall: %v: %v", c.Name, reason)
+ }
+ calls, disabled = target.TransitivelyEnabledCalls(calls)
+ for c, reason := range disabled {
+ log.Logf(0, "transitively unsupported: %v: %v", c.Name, reason)
+ }
+ return calls
+}
diff --git a/tools/syz-stress/stress.go b/tools/syz-stress/stress.go
deleted file mode 100644
index 080c8615e..000000000
--- a/tools/syz-stress/stress.go
+++ /dev/null
@@ -1,182 +0,0 @@
-// 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.
-
-// syz-stress executes random programs locally.
-// A user needs to start a VM manually, copy syz-stress and run it.
-// syz-stress will execute random programs infinitely until it's stopped or it crashes the kernel underneath.
-// If it's given a corpus of programs, it will alternate between executing random programs and mutated
-// programs from the corpus. Running syz-stress can be used as an intermediate step when porting syzkaller
-// to a new OS, or when testing on a machine that is not supported by the vm package (as syz-manager cannot be used).
-package main
-
-import (
- "flag"
- "fmt"
- "math/rand"
- "os"
- "runtime"
- "strings"
- "sync"
- "sync/atomic"
- "time"
-
- "github.com/google/syzkaller/pkg/csource"
- "github.com/google/syzkaller/pkg/db"
- "github.com/google/syzkaller/pkg/host"
- "github.com/google/syzkaller/pkg/ipc"
- "github.com/google/syzkaller/pkg/ipc/ipcconfig"
- "github.com/google/syzkaller/pkg/log"
- "github.com/google/syzkaller/pkg/mgrconfig"
- "github.com/google/syzkaller/prog"
- _ "github.com/google/syzkaller/sys"
-)
-
-var (
- flagOS = flag.String("os", runtime.GOOS, "target os")
- flagArch = flag.String("arch", runtime.GOARCH, "target arch")
- flagCorpus = flag.String("corpus", "", "corpus database")
- flagOutput = flag.Bool("output", false, "print executor output to console")
- flagProcs = flag.Int("procs", 2*runtime.NumCPU(), "number of parallel processes")
- flagLogProg = flag.Bool("logprog", false, "print programs before execution")
- flagGenerate = flag.Bool("generate", true, "generate new programs, otherwise only mutate corpus")
- flagSyscalls = flag.String("syscalls", "", "comma-separated list of enabled syscalls")
- flagEnable = flag.String("enable", "none", "enable only listed additional features")
- flagDisable = flag.String("disable", "none", "enable all additional features except listed")
-
- statExec uint64
- gate *ipc.Gate
-)
-
-func main() {
- flag.Usage = func() {
- flag.PrintDefaults()
- csource.PrintAvailableFeaturesFlags()
- }
- flag.Parse()
- featuresFlags, err := csource.ParseFeaturesFlags(*flagEnable, *flagDisable, true)
- if err != nil {
- log.Fatalf("%v", err)
- }
- target, err := prog.GetTarget(*flagOS, *flagArch)
- if err != nil {
- log.Fatalf("%v", err)
- }
- corpus, err := db.ReadCorpus(*flagCorpus, target)
- if err != nil {
- log.Fatalf("failed to read corpus: %v", err)
- }
- log.Logf(0, "parsed %v programs", len(corpus))
- if !*flagGenerate && len(corpus) == 0 {
- log.Fatalf("nothing to mutate (-generate=false and no corpus)")
- }
-
- features, err := host.Check(target)
- if err != nil {
- log.Fatalf("%v", err)
- }
-
- var syscalls []string
- if *flagSyscalls != "" {
- syscalls = strings.Split(*flagSyscalls, ",")
- }
- calls := buildCallList(target, syscalls)
- ct := target.BuildChoiceTable(corpus, calls)
-
- config, execOpts, err := createIPCConfig(target, features, featuresFlags)
- if err != nil {
- log.Fatalf("%v", err)
- }
- if err = host.Setup(target, features, featuresFlags, config.Executor); err != nil {
- log.Fatal(err)
- }
- gate = ipc.NewGate(2**flagProcs, nil)
- for pid := 0; pid < *flagProcs; pid++ {
- pid := pid
- go func() {
- env, err := ipc.MakeEnv(config, pid)
- if err != nil {
- log.Fatalf("failed to create execution environment: %v", err)
- }
- rs := rand.NewSource(time.Now().UnixNano() + int64(pid)*1e12)
- rnd := rand.New(rs)
- for i := 0; ; i++ {
- var p *prog.Prog
- if *flagGenerate && len(corpus) == 0 || i%4 != 0 {
- p = target.Generate(rs, prog.RecommendedCalls, ct)
- execute(pid, env, execOpts, p)
- p.Mutate(rs, prog.RecommendedCalls, ct, nil, corpus)
- execute(pid, env, execOpts, p)
- } else {
- p = corpus[rnd.Intn(len(corpus))].Clone()
- p.Mutate(rs, prog.RecommendedCalls, ct, nil, corpus)
- execute(pid, env, execOpts, p)
- p.Mutate(rs, prog.RecommendedCalls, ct, nil, corpus)
- execute(pid, env, execOpts, p)
- }
- }
- }()
- }
- for range time.NewTicker(5 * time.Second).C {
- log.Logf(0, "executed %v programs", atomic.LoadUint64(&statExec))
- }
-}
-
-var outMu sync.Mutex
-
-func execute(pid int, env *ipc.Env, execOpts *ipc.ExecOpts, p *prog.Prog) {
- atomic.AddUint64(&statExec, 1)
- if *flagLogProg {
- ticket := gate.Enter()
- defer gate.Leave(ticket)
- outMu.Lock()
- fmt.Printf("executing program %v\n%s\n", pid, p.Serialize())
- outMu.Unlock()
- }
- output, _, hanged, err := env.Exec(execOpts, p)
- if err != nil {
- fmt.Printf("failed to execute executor: %v\n", err)
- }
- if hanged || err != nil || *flagOutput {
- fmt.Printf("PROGRAM:\n%s\n", p.Serialize())
- }
- if hanged || err != nil || *flagOutput {
- os.Stdout.Write(output)
- }
-}
-
-func createIPCConfig(target *prog.Target, features *host.Features, featuresFlags csource.Features) (
- *ipc.Config, *ipc.ExecOpts, error) {
- config, execOpts, err := ipcconfig.Default(target)
- if err != nil {
- return nil, nil, err
- }
- execOpts.EnvFlags |= ipc.FeaturesToFlags(features, featuresFlags)
- return config, execOpts, nil
-}
-
-func buildCallList(target *prog.Target, enabled []string) map[*prog.Syscall]bool {
- enabledSyscalls := make(map[*prog.Syscall]bool)
- if len(enabled) != 0 {
- syscallsIDs, err := mgrconfig.ParseEnabledSyscalls(target, enabled, nil)
- if err != nil {
- log.Fatalf("failed to parse enabled syscalls: %v", err)
- }
- for _, id := range syscallsIDs {
- enabledSyscalls[target.Syscalls[id]] = true
- }
- }
-
- calls, disabled, err := host.DetectSupportedSyscalls(target, "none", enabledSyscalls)
- if err != nil {
- log.Fatalf("failed to detect host supported syscalls: %v", err)
- }
-
- for c, reason := range disabled {
- log.Logf(0, "unsupported syscall: %v: %v", c.Name, reason)
- }
- calls, disabled = target.TransitivelyEnabledCalls(calls)
- for c, reason := range disabled {
- log.Logf(0, "transitively unsupported: %v: %v", c.Name, reason)
- }
- return calls
-}