aboutsummaryrefslogtreecommitdiff
path: root/tools/syz-bisect/bisect.go
blob: e86bb9afaf5ce0dd8c4affc664ba90b62fad9b48 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
// Copyright 2018 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-bisect runs bisection to find cause/fix commit for a crash.
//
// The tool is originally created to test pkg/bisect logic,
// the interface is not particularly handy to use.
//
// The tool requires a config file passed in -config flag, see Config type below for details,
// and a directory with info about the crash passed in -crash flag).
// If -fix flag is specified, it does fix bisection. Otherwise it does cause bisection.
//
// The crash dir should contain the following files:
//  - repro.c: C reproducer for the crash (optional)
//  - repro.syz: syzkaller reproducer for the crash
//  - repro.opts: syzkaller reproducer options (e.g. {"procs":1,"sandbox":"none",...})
//  - syzkaller.commit: hash of syzkaller commit which was used to trigger the crash
//  - kernel.commit: hash of kernel commit on which the crash was triggered
//  - kernel.config: kernel config file
package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"github.com/google/syzkaller/pkg/bisect"
	"github.com/google/syzkaller/pkg/config"
	"github.com/google/syzkaller/pkg/mgrconfig"
)

var (
	flagConfig = flag.String("config", "", "bisect config file")
	flagCrash  = flag.String("crash", "", "dir with crash info")
	flagFix    = flag.Bool("fix", false, "search for crash fix")
)

type Config struct {
	// BinDir must point to a dir that contains compilers required to build
	// older versions of the kernel. For linux, it needs to include several
	// gcc versions. A working archive can be downloaded from:
	// https://storage.googleapis.com/syzkaller/bisect_bin.tar.gz
	BinDir        string `json:"bin_dir"`
	KernelRepo    string `json:"kernel_repo"`
	KernelBranch  string `json:"kernel_branch"`
	SyzkallerRepo string `json:"syzkaller_repo"`
	// Directory with user-space system for building kernel images
	// (for linux that's the input to tools/create-gce-image.sh).
	Userspace string `json:"userspace"`
	// Sysctl/cmdline files used to build the image which was used to crash the kernel, e.g. see:
	// dashboard/config/upstream.sysctl
	// dashboard/config/upstream-selinux.cmdline
	Sysctl  string `json:"sysctl"`
	Cmdline string `json:"cmdline"`
	// Manager config that was used to obtain the crash.
	Manager json.RawMessage `json:"manager"`
}

func main() {
	flag.Parse()
	os.Setenv("SYZ_DISABLE_SANDBOXING", "yes")
	mycfg := new(Config)
	if err := config.LoadFile(*flagConfig, mycfg); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
	mgrcfg, err := mgrconfig.LoadPartialData(mycfg.Manager)
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
	if mgrcfg.Workdir == "" {
		mgrcfg.Workdir, err = ioutil.TempDir("", "syz-bisect")
		if err != nil {
			fmt.Fprintf(os.Stderr, "failed to create temp dir: %v\n", err)
			os.Exit(1)
		}
		defer os.RemoveAll(mgrcfg.Workdir)
	}
	cfg := &bisect.Config{
		Trace:    os.Stdout,
		Fix:      *flagFix,
		BinDir:   mycfg.BinDir,
		DebugDir: *flagCrash,
		Kernel: bisect.KernelConfig{
			Repo:      mycfg.KernelRepo,
			Branch:    mycfg.KernelBranch,
			Userspace: mycfg.Userspace,
			Sysctl:    mycfg.Sysctl,
			Cmdline:   mycfg.Cmdline,
		},
		Syzkaller: bisect.SyzkallerConfig{
			Repo: mycfg.SyzkallerRepo,
		},
		Manager: *mgrcfg,
	}
	loadString("syzkaller.commit", &cfg.Syzkaller.Commit)
	loadString("kernel.commit", &cfg.Kernel.Commit)
	loadFile("kernel.config", &cfg.Kernel.Config)
	loadFile("repro.syz", &cfg.Repro.Syz)
	loadFile("repro.opts", &cfg.Repro.Opts)
	if _, _, err := bisect.Run(cfg); err != nil {
		fmt.Fprintf(os.Stderr, "bisection failed: %v\n", err)
		os.Exit(1)
	}
}

func loadString(file string, dst *string) {
	data, err := ioutil.ReadFile(filepath.Join(*flagCrash, file))
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
	*dst = strings.TrimSpace(string(data))
}

func loadFile(file string, dst *[]byte) {
	data, err := ioutil.ReadFile(filepath.Join(*flagCrash, file))
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
	*dst = data
}