aboutsummaryrefslogtreecommitdiffstats
path: root/pkg
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2018-08-01 21:35:13 +0200
committerDmitry Vyukov <dvyukov@google.com>2018-08-02 16:57:32 +0200
commitfbedd425b575868124577640fe02c9c0a73c4be3 (patch)
tree80b942c87b1b68274573b3ef90a7091211e4769e /pkg
parentd5b1542a54ed37d8b7c9a62bfc441893b509b198 (diff)
pkg/mgrconfig: move from syz-manager/mgrconfig
mgrconfig was used only by syz-manager initially, but now it's used by a dozen of packages and it's weird to import from under a binary dir. pkg/ is much more reasonable dir for a widely used helper package.
Diffstat (limited to 'pkg')
-rw-r--r--pkg/bisect/bisect.go2
-rw-r--r--pkg/csource/options.go2
-rw-r--r--pkg/instance/instance.go2
-rw-r--r--pkg/mgrconfig/mgrconfig.go310
-rw-r--r--pkg/mgrconfig/mgrconfig_test.go64
-rw-r--r--pkg/mgrconfig/testdata/gce1.cfg16
-rw-r--r--pkg/mgrconfig/testdata/gce2.cfg17
-rw-r--r--pkg/mgrconfig/testdata/qemu.cfg20
-rw-r--r--pkg/report/linux_test.go2
-rw-r--r--pkg/report/report.go2
-rw-r--r--pkg/report/report_test.go2
-rw-r--r--pkg/repro/repro.go2
12 files changed, 434 insertions, 7 deletions
diff --git a/pkg/bisect/bisect.go b/pkg/bisect/bisect.go
index bd9501084..c78508368 100644
--- a/pkg/bisect/bisect.go
+++ b/pkg/bisect/bisect.go
@@ -11,9 +11,9 @@ import (
"github.com/google/syzkaller/pkg/build"
"github.com/google/syzkaller/pkg/instance"
+ "github.com/google/syzkaller/pkg/mgrconfig"
"github.com/google/syzkaller/pkg/osutil"
"github.com/google/syzkaller/pkg/vcs"
- "github.com/google/syzkaller/syz-manager/mgrconfig"
)
type Config struct {
diff --git a/pkg/csource/options.go b/pkg/csource/options.go
index 061b20246..1cfc627ee 100644
--- a/pkg/csource/options.go
+++ b/pkg/csource/options.go
@@ -9,7 +9,7 @@ import (
"errors"
"fmt"
- "github.com/google/syzkaller/syz-manager/mgrconfig"
+ "github.com/google/syzkaller/pkg/mgrconfig"
)
// Options control various aspects of source generation.
diff --git a/pkg/instance/instance.go b/pkg/instance/instance.go
index ff3240f52..102d90ca5 100644
--- a/pkg/instance/instance.go
+++ b/pkg/instance/instance.go
@@ -18,11 +18,11 @@ import (
"github.com/google/syzkaller/pkg/build"
"github.com/google/syzkaller/pkg/csource"
"github.com/google/syzkaller/pkg/log"
+ "github.com/google/syzkaller/pkg/mgrconfig"
"github.com/google/syzkaller/pkg/osutil"
"github.com/google/syzkaller/pkg/report"
"github.com/google/syzkaller/pkg/vcs"
"github.com/google/syzkaller/prog"
- "github.com/google/syzkaller/syz-manager/mgrconfig"
"github.com/google/syzkaller/vm"
)
diff --git a/pkg/mgrconfig/mgrconfig.go b/pkg/mgrconfig/mgrconfig.go
new file mode 100644
index 000000000..1b2052838
--- /dev/null
+++ b/pkg/mgrconfig/mgrconfig.go
@@ -0,0 +1,310 @@
+// 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 mgrconfig
+
+import (
+ "encoding/json"
+ "fmt"
+ "os"
+ "path/filepath"
+ "strings"
+
+ "github.com/google/syzkaller/pkg/config"
+ "github.com/google/syzkaller/pkg/osutil"
+ "github.com/google/syzkaller/prog"
+ _ "github.com/google/syzkaller/sys" // most mgrconfig users want targets too
+ "github.com/google/syzkaller/sys/targets"
+)
+
+type Config struct {
+ // Instance name (used for identification and as GCE instance prefix).
+ Name string `json:"name"`
+ // Target OS/arch, e.g. "linux/arm64" or "linux/amd64/386" (amd64 OS with 386 test process).
+ Target string `json:"target"`
+ // TCP address to serve HTTP stats page (e.g. "localhost:50000").
+ HTTP string `json:"http"`
+ // TCP address to serve RPC for fuzzer processes (optional).
+ RPC string `json:"rpc"`
+ Workdir string `json:"workdir"`
+ // Directory with kernel object files.
+ KernelObj string `json:"kernel_obj"`
+ // Kernel source directory (if not set defaults to KernelObj).
+ KernelSrc string `json:"kernel_src"`
+ // Arbitrary optional tag that is saved along with crash reports (e.g. branch/commit).
+ Tag string `json:"tag"`
+ // Linux image for VMs.
+ Image string `json:"image"`
+ // SSH key for the image (may be empty for some VM types).
+ SSHKey string `json:"sshkey"`
+ // SSH user ("root" by default).
+ SSHUser string `json:"ssh_user"`
+
+ HubClient string `json:"hub_client"`
+ HubAddr string `json:"hub_addr"`
+ HubKey string `json:"hub_key"`
+
+ // syz-manager will send crash emails to this list of emails using mailx (optional).
+ EmailAddrs []string `json:"email_addrs"`
+
+ DashboardClient string `json:"dashboard_client"`
+ DashboardAddr string `json:"dashboard_addr"`
+ DashboardKey string `json:"dashboard_key"`
+
+ // Path to syzkaller checkout (syz-manager will look for binaries in bin subdir).
+ Syzkaller string `json:"syzkaller"`
+ // Number of parallel processes inside of every VM.
+ Procs int `json:"procs"`
+
+ // Type of sandbox to use during fuzzing:
+ // "none": don't do anything special (has false positives, e.g. due to killing init), default
+ // "setuid": impersonate into user nobody (65534)
+ // "namespace": create a new namespace for fuzzer using CLONE_NEWNS/CLONE_NEWNET/CLONE_NEWPID/etc,
+ // requires building kernel with CONFIG_NAMESPACES, CONFIG_UTS_NS, CONFIG_USER_NS,
+ // CONFIG_PID_NS and CONFIG_NET_NS.
+ Sandbox string `json:"sandbox"`
+
+ // Use KCOV coverage (default: true).
+ Cover bool `json:"cover"`
+ // Reproduce, localize and minimize crashers (default: true).
+ Reproduce bool `json:"reproduce"`
+
+ EnabledSyscalls []string `json:"enable_syscalls"`
+ DisabledSyscalls []string `json:"disable_syscalls"`
+ // Don't save reports matching these regexps, but reboot VM after them,
+ // matched against whole report output.
+ Suppressions []string `json:"suppressions"`
+ // Completely ignore reports matching these regexps (don't save nor reboot),
+ // must match the first line of crash message.
+ Ignores []string `json:"ignores"`
+
+ // VM type (qemu, gce, android, isolated, etc).
+ Type string `json:"type"`
+ // VM-type-specific config.
+ VM json.RawMessage `json:"vm"`
+
+ // Implementation details beyond this point.
+ // Parsed Target:
+ TargetOS string `json:"-"`
+ TargetArch string `json:"-"`
+ TargetVMArch string `json:"-"`
+ // Syzkaller binaries that we are going to use:
+ SyzFuzzerBin string `json:"-"`
+ SyzExecprogBin string `json:"-"`
+ SyzExecutorBin string `json:"-"`
+}
+
+func LoadData(data []byte) (*Config, error) {
+ cfg, err := LoadPartialData(data)
+ if err != nil {
+ return nil, err
+ }
+ if err := Complete(cfg); err != nil {
+ return nil, err
+ }
+ return cfg, nil
+}
+
+func LoadFile(filename string) (*Config, error) {
+ cfg, err := LoadPartialFile(filename)
+ if err != nil {
+ return nil, err
+ }
+ if err := Complete(cfg); err != nil {
+ return nil, err
+ }
+ return cfg, nil
+}
+
+func LoadPartialData(data []byte) (*Config, error) {
+ cfg := defaultValues()
+ if err := config.LoadData(data, cfg); err != nil {
+ return nil, err
+ }
+ return loadPartial(cfg)
+}
+
+func LoadPartialFile(filename string) (*Config, error) {
+ cfg := defaultValues()
+ if err := config.LoadFile(filename, cfg); err != nil {
+ return nil, err
+ }
+ return loadPartial(cfg)
+}
+
+func defaultValues() *Config {
+ return &Config{
+ SSHUser: "root",
+ Cover: true,
+ Reproduce: true,
+ Sandbox: "none",
+ RPC: ":0",
+ Procs: 1,
+ }
+}
+
+func loadPartial(cfg *Config) (*Config, error) {
+ var err error
+ cfg.TargetOS, cfg.TargetVMArch, cfg.TargetArch, err = splitTarget(cfg.Target)
+ if err != nil {
+ return nil, err
+ }
+ return cfg, nil
+}
+
+func Complete(cfg *Config) error {
+ if cfg.TargetOS == "" || cfg.TargetVMArch == "" || cfg.TargetArch == "" {
+ return fmt.Errorf("target parameters are not filled in")
+ }
+ if cfg.Workdir == "" {
+ return fmt.Errorf("config param workdir is empty")
+ }
+ cfg.Workdir = osutil.Abs(cfg.Workdir)
+ if cfg.Syzkaller == "" {
+ return fmt.Errorf("config param syzkaller is empty")
+ }
+ if err := completeBinaries(cfg); err != nil {
+ return err
+ }
+ if cfg.HTTP == "" {
+ return fmt.Errorf("config param http is empty")
+ }
+ if cfg.Type == "" {
+ return fmt.Errorf("config param type is empty")
+ }
+ if cfg.Procs < 1 || cfg.Procs > 32 {
+ return fmt.Errorf("bad config param procs: '%v', want [1, 32]", cfg.Procs)
+ }
+ switch cfg.Sandbox {
+ case "none", "setuid", "namespace":
+ default:
+ return fmt.Errorf("config param sandbox must contain one of none/setuid/namespace")
+ }
+ if err := checkSSHParams(cfg); err != nil {
+ return err
+ }
+
+ cfg.KernelObj = osutil.Abs(cfg.KernelObj)
+ if cfg.KernelSrc == "" {
+ cfg.KernelSrc = cfg.KernelObj // assume in-tree build by default
+ }
+ cfg.KernelSrc = osutil.Abs(cfg.KernelSrc)
+ if cfg.HubClient != "" && (cfg.Name == "" || cfg.HubAddr == "" || cfg.HubKey == "") {
+ return fmt.Errorf("hub_client is set, but name/hub_addr/hub_key is empty")
+ }
+ if cfg.DashboardClient != "" && (cfg.Name == "" ||
+ cfg.DashboardAddr == "" ||
+ cfg.DashboardKey == "") {
+ return fmt.Errorf("dashboard_client is set, but name/dashboard_addr/dashboard_key is empty")
+ }
+
+ return nil
+}
+
+func checkSSHParams(cfg *Config) error {
+ if cfg.SSHUser == "" {
+ return fmt.Errorf("bad config syzkaller param: ssh user is empty")
+ }
+ if cfg.SSHKey == "" {
+ return nil
+ }
+ info, err := os.Stat(cfg.SSHKey)
+ if err != nil {
+ return err
+ }
+ if info.Mode()&0077 != 0 {
+ return fmt.Errorf("sshkey %v is unprotected, ssh will reject it, do chmod 0600", cfg.SSHKey)
+ }
+ return nil
+}
+
+func completeBinaries(cfg *Config) error {
+ sysTarget := targets.Get(cfg.TargetOS, cfg.TargetArch)
+ if sysTarget == nil {
+ return fmt.Errorf("unsupported OS/arch: %v/%v", cfg.TargetOS, cfg.TargetArch)
+ }
+ cfg.Syzkaller = osutil.Abs(cfg.Syzkaller)
+ exe := sysTarget.ExeExtension
+ targetBin := func(name, arch string) string {
+ return filepath.Join(cfg.Syzkaller, "bin", cfg.TargetOS+"_"+arch, name+exe)
+ }
+ cfg.SyzFuzzerBin = targetBin("syz-fuzzer", cfg.TargetVMArch)
+ cfg.SyzExecprogBin = targetBin("syz-execprog", cfg.TargetVMArch)
+ cfg.SyzExecutorBin = targetBin("syz-executor", cfg.TargetArch)
+ if !osutil.IsExist(cfg.SyzFuzzerBin) {
+ return fmt.Errorf("bad config syzkaller param: can't find %v", cfg.SyzFuzzerBin)
+ }
+ if !osutil.IsExist(cfg.SyzExecprogBin) {
+ return fmt.Errorf("bad config syzkaller param: can't find %v", cfg.SyzExecprogBin)
+ }
+ if !osutil.IsExist(cfg.SyzExecutorBin) {
+ return fmt.Errorf("bad config syzkaller param: can't find %v", cfg.SyzExecutorBin)
+ }
+ return nil
+}
+
+func splitTarget(target string) (string, string, string, error) {
+ if target == "" {
+ return "", "", "", fmt.Errorf("target is empty")
+ }
+ targetParts := strings.Split(target, "/")
+ if len(targetParts) != 2 && len(targetParts) != 3 {
+ return "", "", "", fmt.Errorf("bad config param target")
+ }
+ os := targetParts[0]
+ vmarch := targetParts[1]
+ arch := targetParts[1]
+ if len(targetParts) == 3 {
+ arch = targetParts[2]
+ }
+ return os, vmarch, arch, nil
+}
+
+func ParseEnabledSyscalls(target *prog.Target, enabled, disabled []string) (map[int]bool, error) {
+ syscalls := make(map[int]bool)
+ if len(enabled) != 0 {
+ for _, c := range enabled {
+ n := 0
+ for _, call := range target.Syscalls {
+ if matchSyscall(call.Name, c) {
+ syscalls[call.ID] = true
+ n++
+ }
+ }
+ if n == 0 {
+ return nil, fmt.Errorf("unknown enabled syscall: %v", c)
+ }
+ }
+ } else {
+ for _, call := range target.Syscalls {
+ syscalls[call.ID] = true
+ }
+ }
+ for _, c := range disabled {
+ n := 0
+ for _, call := range target.Syscalls {
+ if matchSyscall(call.Name, c) {
+ delete(syscalls, call.ID)
+ n++
+ }
+ }
+ if n == 0 {
+ return nil, fmt.Errorf("unknown disabled syscall: %v", c)
+ }
+ }
+ if len(syscalls) == 0 {
+ return nil, fmt.Errorf("all syscalls are disabled by disable_syscalls in config")
+ }
+ return syscalls, nil
+}
+
+func matchSyscall(name, pattern string) bool {
+ if pattern == name || strings.HasPrefix(name, pattern+"$") {
+ return true
+ }
+ if len(pattern) > 1 && pattern[len(pattern)-1] == '*' &&
+ strings.HasPrefix(name, pattern[:len(pattern)-1]) {
+ return true
+ }
+ return false
+}
diff --git a/pkg/mgrconfig/mgrconfig_test.go b/pkg/mgrconfig/mgrconfig_test.go
new file mode 100644
index 000000000..1e4dc95fd
--- /dev/null
+++ b/pkg/mgrconfig/mgrconfig_test.go
@@ -0,0 +1,64 @@
+// Copyright 2017 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 mgrconfig
+
+import (
+ "path/filepath"
+ "testing"
+
+ "github.com/google/syzkaller/pkg/config"
+ "github.com/google/syzkaller/vm/gce"
+ "github.com/google/syzkaller/vm/qemu"
+)
+
+func TestCanned(t *testing.T) {
+ files, err := filepath.Glob(filepath.Join("testdata", "*.cfg"))
+ if err != nil || len(files) == 0 {
+ t.Fatalf("failed to read input files: %v", err)
+ }
+ for _, file := range files {
+ t.Run(file, func(t *testing.T) {
+ cfg := new(Config)
+ if err := config.LoadFile(file, cfg); err != nil {
+ t.Fatal(err)
+ }
+ var vmCfg interface{}
+ switch cfg.Type {
+ case "qemu":
+ vmCfg = new(qemu.Config)
+ case "gce":
+ vmCfg = new(gce.Config)
+ default:
+ t.Fatalf("unknown VM type: %v", cfg.Type)
+ }
+ if err := config.LoadData(cfg.VM, vmCfg); err != nil {
+ t.Fatalf("failed to load %v config: %v", cfg.Type, err)
+ }
+ })
+ }
+}
+
+func TestMatchSyscall(t *testing.T) {
+ tests := []struct {
+ pattern string
+ call string
+ result bool
+ }{
+ {"foo", "foo", true},
+ {"foo", "bar", false},
+ {"foo", "foo$BAR", true},
+ {"foo*", "foo", true},
+ {"foo*", "foobar", true},
+ {"foo*", "foo$BAR", true},
+ {"foo$*", "foo", false},
+ {"foo$*", "foo$BAR", true},
+ }
+ for i, test := range tests {
+ res := matchSyscall(test.call, test.pattern)
+ if res != test.result {
+ t.Errorf("#%v: pattern=%q call=%q want=%v got=%v",
+ i, test.pattern, test.call, test.result, res)
+ }
+ }
+}
diff --git a/pkg/mgrconfig/testdata/gce1.cfg b/pkg/mgrconfig/testdata/gce1.cfg
new file mode 100644
index 000000000..44b4eae12
--- /dev/null
+++ b/pkg/mgrconfig/testdata/gce1.cfg
@@ -0,0 +1,16 @@
+{
+ "name": "windows-gce",
+ "target": "windows/amd64",
+ "http": ":10000",
+ "workdir": "/workdir",
+ "syzkaller": "/syzkaller",
+ "sshkey": "/syzkaller_id_rsa",
+ "ssh_user": "syzkaller",
+ "procs": 8,
+ "type": "gce",
+ "vm": {
+ "count": 10,
+ "machine_type": "n1-highcpu-2",
+ "gce_image": "gce-image-name"
+ }
+}
diff --git a/pkg/mgrconfig/testdata/gce2.cfg b/pkg/mgrconfig/testdata/gce2.cfg
new file mode 100644
index 000000000..cf9a620f2
--- /dev/null
+++ b/pkg/mgrconfig/testdata/gce2.cfg
@@ -0,0 +1,17 @@
+{
+ "name": "linux-gce",
+ "target": "linux/amd64",
+ "http": ":10000",
+ "workdir": "/workdir",
+ "syzkaller": "/syzkaller",
+ "image": "/local/path/to/disk.raw",
+ "sshkey": "/syzkaller_id_rsa",
+ "ssh_user": "syzkaller",
+ "procs": 8,
+ "type": "gce",
+ "vm": {
+ "count": 10,
+ "machine_type": "n1-highcpu-2",
+ "gcs_path": "gce-bucket-name-to-upload-image"
+ }
+}
diff --git a/pkg/mgrconfig/testdata/qemu.cfg b/pkg/mgrconfig/testdata/qemu.cfg
new file mode 100644
index 000000000..f390537fd
--- /dev/null
+++ b/pkg/mgrconfig/testdata/qemu.cfg
@@ -0,0 +1,20 @@
+{
+ "target": "linux/amd64",
+ "http": "myhost.com:56741",
+ "workdir": "/syzkaller/workdir",
+ "kernel_obj": "/linux/",
+ "image": "/linux_image/wheezy.img",
+ "sshkey": "/linux_image/ssh/id_rsa",
+ "syzkaller": "/syzkaller",
+ "disable_syscalls": ["keyctl", "add_key", "request_key"],
+ "suppressions": ["some known bug"],
+ "procs": 4,
+ "type": "qemu",
+ "vm": {
+ "count": 16,
+ "cpu": 2,
+ "mem": 2048,
+ "kernel": "/linux/arch/x86/boot/bzImage",
+ "initrd": "linux/initrd"
+ }
+}
diff --git a/pkg/report/linux_test.go b/pkg/report/linux_test.go
index 162f6638e..83b631f55 100644
--- a/pkg/report/linux_test.go
+++ b/pkg/report/linux_test.go
@@ -7,8 +7,8 @@ import (
"fmt"
"testing"
+ "github.com/google/syzkaller/pkg/mgrconfig"
"github.com/google/syzkaller/pkg/symbolizer"
- "github.com/google/syzkaller/syz-manager/mgrconfig"
)
func TestLinuxIgnores(t *testing.T) {
diff --git a/pkg/report/report.go b/pkg/report/report.go
index 1a6e76505..4a7c43145 100644
--- a/pkg/report/report.go
+++ b/pkg/report/report.go
@@ -12,7 +12,7 @@ import (
"regexp"
"strings"
- "github.com/google/syzkaller/syz-manager/mgrconfig"
+ "github.com/google/syzkaller/pkg/mgrconfig"
)
type Reporter interface {
diff --git a/pkg/report/report_test.go b/pkg/report/report_test.go
index a68d7d149..7012b54f2 100644
--- a/pkg/report/report_test.go
+++ b/pkg/report/report_test.go
@@ -15,8 +15,8 @@ import (
"strings"
"testing"
+ "github.com/google/syzkaller/pkg/mgrconfig"
"github.com/google/syzkaller/pkg/osutil"
- "github.com/google/syzkaller/syz-manager/mgrconfig"
)
var flagUpdate = flag.Bool("update", false, "update test files accordingly to current results")
diff --git a/pkg/repro/repro.go b/pkg/repro/repro.go
index 5d6670a20..a10e02f27 100644
--- a/pkg/repro/repro.go
+++ b/pkg/repro/repro.go
@@ -14,10 +14,10 @@ import (
"github.com/google/syzkaller/pkg/csource"
instancePkg "github.com/google/syzkaller/pkg/instance"
"github.com/google/syzkaller/pkg/log"
+ "github.com/google/syzkaller/pkg/mgrconfig"
"github.com/google/syzkaller/pkg/osutil"
"github.com/google/syzkaller/pkg/report"
"github.com/google/syzkaller/prog"
- "github.com/google/syzkaller/syz-manager/mgrconfig"
"github.com/google/syzkaller/vm"
)