<commit_before>// 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"
	"path/filepath"
	"regexp"
	"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/vm"
)

type Config struct {
	Name       string // Instance name (used for identification and as GCE instance prefix)
	Target     string // Target OS/arch, e.g. "linux/arm64" or "linux/amd64/386" (amd64 OS with 386 test process)
	HTTP       string // TCP address to serve HTTP stats page (e.g. "localhost:50000")
	RPC        string // TCP address to serve RPC for fuzzer processes (optional)
	Workdir    string
	Vmlinux    string
	Kernel_Src string // kernel source directory
	Tag        string // arbitrary optional tag that is saved along with crash reports (e.g. branch/commit)
	Image      string // linux image for VMs
	SSHKey     string // ssh key for the image (may be empty for some VM types)
	SSH_User   string // ssh user ("root" by default)

	Hub_Client string
	Hub_Addr   string
	Hub_Key    string

	Email_Addrs []string // syz-manager will send crash emails to this list of emails using mailx (optional)

	Dashboard_Client string
	Dashboard_Addr   string
	Dashboard_Key    string

	Syzkaller string // path to syzkaller checkout (syz-manager will look for binaries in bin subdir)
	Procs     int    // number of parallel processes inside of every VM

	Sandbox string // type of sandbox to use during fuzzing:
	// "none": don't do anything special (has false positives, e.g. due to killing init)
	// "setuid": impersonate into user nobody (65534), default
	// "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.

	Cover     bool // use kcov coverage (default: true)
	Leak      bool // do memory leak checking
	Reproduce bool // reproduce, localize and minimize crashers (on by default)

	Enable_Syscalls  []string
	Disable_Syscalls []string
	Suppressions     []string // don't save reports matching these regexps, but reboot VM after them
	Ignores          []string // completely ignore reports matching these regexps (don't save nor reboot)

	Type string          // VM type (qemu, kvm, local)
	VM   json.RawMessage // VM-type-specific config

	// Implementation details beyond this point.
	ParsedSuppressions []*regexp.Regexp `json:"-"`
	ParsedIgnores      []*regexp.Regexp `json:"-"`
	// 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) {
	return load(data, "")
}

func LoadFile(filename string) (*Config, error) {
	return load(nil, filename)
}

func DefaultValues() *Config {
	return &Config{
		SSH_User:  "root",
		Cover:     true,
		Reproduce: true,
		Sandbox:   "setuid",
		RPC:       ":0",
		Procs:     1,
	}
}

func load(data []byte, filename string) (*Config, error) {
	cfg := DefaultValues()
	if data != nil {
		if err := config.LoadData(data, cfg); err != nil {
			return nil, err
		}
	} else {
		if err := config.LoadFile(filename, cfg); err != nil {
			return nil, err
		}
	}

	var err error
	cfg.TargetOS, cfg.TargetVMArch, cfg.TargetArch, err = SplitTarget(cfg.Target)
	if err != nil {
		return nil, err
	}

	targetBin := func(name, arch string) string {
		exe := ""
		if cfg.TargetOS == "windows" {
			exe = ".exe"
		}
		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 nil, fmt.Errorf("bad config syzkaller param: can't find %v", cfg.SyzFuzzerBin)
	}
	if !osutil.IsExist(cfg.SyzExecprogBin) {
		return nil, fmt.Errorf("bad config syzkaller param: can't find %v", cfg.SyzExecprogBin)
	}
	if !osutil.IsExist(cfg.SyzExecutorBin) {
		return nil, fmt.Errorf("bad config syzkaller param: can't find %v", cfg.SyzExecutorBin)
	}
	if cfg.HTTP == "" {
		return nil, fmt.Errorf("config param http is empty")
	}
	if cfg.Workdir == "" {
		return nil, fmt.Errorf("config param workdir is empty")
	}
	if cfg.Type == "" {
		return nil, fmt.Errorf("config param type is empty")
	}
	if cfg.Procs < 1 || cfg.Procs > 32 {
		return nil, fmt.Errorf("bad config param procs: '%v', want [1, 32]", cfg.Procs)
	}
	switch cfg.Sandbox {
	case "none", "setuid", "namespace":
	default:
		return nil, fmt.Errorf("config param sandbox must contain one of none/setuid/namespace")
	}

	cfg.Workdir = osutil.Abs(cfg.Workdir)
	cfg.Vmlinux = osutil.Abs(cfg.Vmlinux)
	cfg.Syzkaller = osutil.Abs(cfg.Syzkaller)
	if cfg.Kernel_Src == "" {
		cfg.Kernel_Src = filepath.Dir(cfg.Vmlinux) // assume in-tree build by default
	}

	if err := parseSuppressions(cfg); err != nil {<commit_msg>syz-manager/mgrconfig: check that ssh key is properly protected<commit_after>// 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"
	"regexp"
	"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/vm"
)

type Config struct {
	Name       string // Instance name (used for identification and as GCE instance prefix)
	Target     string // Target OS/arch, e.g. "linux/arm64" or "linux/amd64/386" (amd64 OS with 386 test process)
	HTTP       string // TCP address to serve HTTP stats page (e.g. "localhost:50000")
	RPC        string // TCP address to serve RPC for fuzzer processes (optional)
	Workdir    string
	Vmlinux    string
	Kernel_Src string // kernel source directory
	Tag        string // arbitrary optional tag that is saved along with crash reports (e.g. branch/commit)
	Image      string // linux image for VMs
	SSHKey     string // ssh key for the image (may be empty for some VM types)
	SSH_User   string // ssh user ("root" by default)

	Hub_Client string
	Hub_Addr   string
	Hub_Key    string

	Email_Addrs []string // syz-manager will send crash emails to this list of emails using mailx (optional)

	Dashboard_Client string
	Dashboard_Addr   string
	Dashboard_Key    string

	Syzkaller string // path to syzkaller checkout (syz-manager will look for binaries in bin subdir)
	Procs     int    // number of parallel processes inside of every VM

	Sandbox string // type of sandbox to use during fuzzing:
	// "none": don't do anything special (has false positives, e.g. due to killing init)
	// "setuid": impersonate into user nobody (65534), default
	// "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.

	Cover     bool // use kcov coverage (default: true)
	Leak      bool // do memory leak checking
	Reproduce bool // reproduce, localize and minimize crashers (on by default)

	Enable_Syscalls  []string
	Disable_Syscalls []string
	Suppressions     []string // don't save reports matching these regexps, but reboot VM after them
	Ignores          []string // completely ignore reports matching these regexps (don't save nor reboot)

	Type string          // VM type (qemu, kvm, local)
	VM   json.RawMessage // VM-type-specific config

	// Implementation details beyond this point.
	ParsedSuppressions []*regexp.Regexp `json:"-"`
	ParsedIgnores      []*regexp.Regexp `json:"-"`
	// 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) {
	return load(data, "")
}

func LoadFile(filename string) (*Config, error) {
	return load(nil, filename)
}

func DefaultValues() *Config {
	return &Config{
		SSH_User:  "root",
		Cover:     true,
		Reproduce: true,
		Sandbox:   "setuid",
		RPC:       ":0",
		Procs:     1,
	}
}

func load(data []byte, filename string) (*Config, error) {
	cfg := DefaultValues()
	if data != nil {
		if err := config.LoadData(data, cfg); err != nil {
			return nil, err
		}
	} else {
		if err := config.LoadFile(filename, cfg); err != nil {
			return nil, err
		}
	}

	var err error
	cfg.TargetOS, cfg.TargetVMArch, cfg.TargetArch, err = SplitTarget(cfg.Target)
	if err != nil {
		return nil, err
	}

	targetBin := func(name, arch string) string {
		exe := ""
		if cfg.TargetOS == "windows" {
			exe = ".exe"
		}
		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 nil, fmt.Errorf("bad config syzkaller param: can't find %v", cfg.SyzFuzzerBin)
	}
	if !osutil.IsExist(cfg.SyzExecprogBin) {
		return nil, fmt.Errorf("bad config syzkaller param: can't find %v", cfg.SyzExecprogBin)
	}
	if !osutil.IsExist(cfg.SyzExecutorBin) {
		return nil, fmt.Errorf("bad config syzkaller param: can't find %v", cfg.SyzExecutorBin)
	}
	if cfg.HTTP == "" {
		return nil, fmt.Errorf("config param http is empty")
	}
	if cfg.Workdir == "" {
		return nil, fmt.Errorf("config param workdir is empty")
	}
	if cfg.Type == "" {
		return nil, fmt.Errorf("config param type is empty")
	}
	if cfg.Procs < 1 || cfg.Procs > 32 {
		return nil, fmt.Errorf("bad config param procs: '%v', want [1, 32]", cfg.Procs)
	}
	switch cfg.Sandbox {
	case "none", "setuid", "namespace":
	default:
		return nil, fmt.Errorf("config param sandbox must contain one of none/setuid/namespace")
	}
	if cfg.SSHKey != "" {
		info, err := os.Stat(cfg.SSHKey)
		if err != nil {
			return nil, err
		}
		if info.Mode()&0077 != 0 {
			return nil, fmt.Errorf("sshkey %v is unprotected, ssh will reject it, do chmod 0600 on it", cfg.SSHKey)
		}
	}

	cfg.Workdir = osutil.Abs(cfg.Workdir)
	cfg.Vmlinux = osutil.Abs(cfg.Vmlinux)
	cfg.Syzkaller = osutil.Abs(cfg.Syzkaller)
	if cfg.Kernel_Src == "" {
		cfg.Kernel_Src = filepath.Dir(cfg.Vmlinux) // assume in-tree build by default
	}

	if err := parseSuppressions(cfg); err != nil {
{'fraction_non_alphanumeric': 0.11735342167362497, 'fraction_numerical': 0.007170085314939191, 'mean_word_length': 3.596996245306633, 'pattern_counts': {'":': 10, '<': 5, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1538769', 'n_tokens_mistral': 3926, 'n_tokens_neox': 3720, 'n_words': 1172}