diff options
| author | Dmitry Vyukov <dvyukov@google.com> | 2018-08-01 21:35:13 +0200 |
|---|---|---|
| committer | Dmitry Vyukov <dvyukov@google.com> | 2018-08-02 16:57:32 +0200 |
| commit | fbedd425b575868124577640fe02c9c0a73c4be3 (patch) | |
| tree | 80b942c87b1b68274573b3ef90a7091211e4769e /pkg | |
| parent | d5b1542a54ed37d8b7c9a62bfc441893b509b198 (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.go | 2 | ||||
| -rw-r--r-- | pkg/csource/options.go | 2 | ||||
| -rw-r--r-- | pkg/instance/instance.go | 2 | ||||
| -rw-r--r-- | pkg/mgrconfig/mgrconfig.go | 310 | ||||
| -rw-r--r-- | pkg/mgrconfig/mgrconfig_test.go | 64 | ||||
| -rw-r--r-- | pkg/mgrconfig/testdata/gce1.cfg | 16 | ||||
| -rw-r--r-- | pkg/mgrconfig/testdata/gce2.cfg | 17 | ||||
| -rw-r--r-- | pkg/mgrconfig/testdata/qemu.cfg | 20 | ||||
| -rw-r--r-- | pkg/report/linux_test.go | 2 | ||||
| -rw-r--r-- | pkg/report/report.go | 2 | ||||
| -rw-r--r-- | pkg/report/report_test.go | 2 | ||||
| -rw-r--r-- | pkg/repro/repro.go | 2 |
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" ) |
