aboutsummaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2017-09-14 15:25:06 +0200
committerDmitry Vyukov <dvyukov@google.com>2017-09-15 16:02:37 +0200
commit4503776d2bc15a9e2738402760ff731252e4d2ec (patch)
treefed90e104c93c02675e34ca4a4a41498239dd844 /sys
parent7296cf374d9ec92c394edf965d5347742fe5aed9 (diff)
sys/syz-extract: generate build files out of tree
This does not pollute user kernel dir (we do make mrproper, though) and enables parallel generation.
Diffstat (limited to 'sys')
-rw-r--r--sys/syz-extract/extract.go172
-rw-r--r--sys/syz-extract/fetch.go37
2 files changed, 139 insertions, 70 deletions
diff --git a/sys/syz-extract/extract.go b/sys/syz-extract/extract.go
index 707b77e58..52a0ae7f0 100644
--- a/sys/syz-extract/extract.go
+++ b/sys/syz-extract/extract.go
@@ -26,16 +26,31 @@ var (
flagLinux = flag.String("linux", "", "path to linux kernel source checkout")
flagLinuxBld = flag.String("linuxbld", "", "path to linux kernel build directory")
flagArch = flag.String("arch", "", "comma-separated list of arches to generate (all by default)")
- flagBuild = flag.Bool("build", false, "generate arch-specific files in the linux dir")
+ flagBuild = flag.Bool("build", false, "regenerate arch-specific kernel headers")
)
+type Arch struct {
+ target *sys.Target
+ kernelDir string
+ buildDir string
+ build bool
+ files []*File
+ err error
+}
+
type File struct {
+ arch *Arch
name string
undeclared map[string]bool
err error
}
func main() {
+ failf := func(msg string, args ...interface{}) {
+ fmt.Fprintf(os.Stderr, msg+"\n", args...)
+ os.Exit(1)
+ }
+
const OS = "linux"
flag.Parse()
if *flagLinux == "" {
@@ -48,49 +63,105 @@ func main() {
if n == 0 {
failf("usage: syz-extract -linux=/linux/checkout -arch=arch input_file.txt...")
}
- var arches []string
+
+ var archArray []string
if *flagArch != "" {
- arches = strings.Split(*flagArch, ",")
+ archArray = strings.Split(*flagArch, ",")
} else {
for arch := range sys.Targets[OS] {
- arches = append(arches, arch)
+ archArray = append(archArray, arch)
}
- sort.Strings(arches)
+ sort.Strings(archArray)
}
- for _, arch := range arches {
- fmt.Printf("generating %v/%v...\n", OS, arch)
- target := sys.Targets[OS][arch]
- if target == nil {
- failf("unknown arch %v", arch)
+
+ if *flagBuild {
+ // Otherwise out-of-tree build fails.
+ fmt.Printf("make mrproper\n")
+ out, err := osutil.RunCmd(time.Hour, *flagLinux, "make", "mrproper")
+ if err != nil {
+ failf("make mrproper failed: %v\n%s\n", err, out)
}
+ } else {
+ if len(archArray) > 1 {
+ failf("more than 1 arch is invalid without -build")
+ }
+ }
+
+ jobC := make(chan interface{}, len(archArray)*len(flag.Args()))
+ var wg sync.WaitGroup
+
+ var arches []*Arch
+ for _, archStr := range archArray {
+ buildDir := ""
if *flagBuild {
- buildKernel(target, *flagLinux)
- *flagLinuxBld = *flagLinux
- } else if *flagLinuxBld == "" {
- *flagLinuxBld = *flagLinux
+ dir, err := ioutil.TempDir("", "syzkaller-kernel-build")
+ if err != nil {
+ failf("failed to create temp dir: %v", err)
+ }
+ buildDir = dir
+ } else if *flagLinuxBld != "" {
+ buildDir = *flagLinuxBld
+ } else {
+ buildDir = *flagLinux
+ }
+
+ target := sys.Targets[OS][archStr]
+ if target == nil {
+ failf("unknown arch: %v", archStr)
}
- files := make([]File, n)
- inc := make(chan *File, n)
- for i, f := range flag.Args() {
- files[i].name = f
- inc <- &files[i]
+ arch := &Arch{
+ target: target,
+ kernelDir: *flagLinux,
+ buildDir: buildDir,
+ build: *flagBuild,
}
- close(inc)
-
- procs := runtime.GOMAXPROCS(0)
- var wg sync.WaitGroup
- wg.Add(procs)
- for p := 0; p < procs; p++ {
- go func() {
- defer wg.Done()
- for f := range inc {
- f.undeclared, f.err = processFile(target, f.name)
+ for _, f := range flag.Args() {
+ arch.files = append(arch.files, &File{
+ arch: arch,
+ name: f,
+ })
+ }
+ arches = append(arches, arch)
+ jobC <- arch
+ wg.Add(1)
+ }
+
+ for p := 0; p < runtime.GOMAXPROCS(0); p++ {
+ go func() {
+ for job := range jobC {
+ switch j := job.(type) {
+ case *Arch:
+ if j.build {
+ j.err = buildKernel(j)
+ }
+ if j.err == nil {
+ for _, f := range j.files {
+ wg.Add(1)
+ jobC <- f
+ }
+ }
+ case *File:
+ j.undeclared, j.err = processFile(j.arch, j.name)
}
- }()
+ wg.Done()
+ }
+ }()
+ }
+ wg.Wait()
+
+ for _, arch := range arches {
+ if arch.build {
+ os.RemoveAll(arch.buildDir)
}
- wg.Wait()
- for _, f := range files {
+ }
+
+ for _, arch := range arches {
+ fmt.Printf("generating %v/%v...\n", arch.target.OS, arch.target.Arch)
+ if arch.err != nil {
+ failf("%v", arch.err)
+ }
+ for _, f := range arch.files {
fmt.Printf("extracting from %v\n", f.name)
if f.err != nil {
failf("%v", f.err)
@@ -103,8 +174,8 @@ func main() {
}
}
-func processFile(target *sys.Target, inname string) (map[string]bool, error) {
- outname := strings.TrimSuffix(inname, ".txt") + "_" + target.Arch + ".const"
+func processFile(arch *Arch, inname string) (map[string]bool, error) {
+ outname := strings.TrimSuffix(inname, ".txt") + "_" + arch.target.Arch + ".const"
indata, err := ioutil.ReadFile(inname)
if err != nil {
return nil, fmt.Errorf("failed to read input file: %v", err)
@@ -125,7 +196,7 @@ func processFile(target *sys.Target, inname string) (map[string]bool, error) {
return nil, nil
}
includes := append(info.Includes, "asm/unistd.h")
- consts, undeclared, err := fetchValues(target, info.Consts, includes, info.Incdirs, info.Defines)
+ consts, undeclared, err := fetchValues(arch.target, arch.kernelDir, arch.buildDir, info.Consts, includes, info.Incdirs, info.Defines)
if err != nil {
return nil, err
}
@@ -136,36 +207,33 @@ func processFile(target *sys.Target, inname string) (map[string]bool, error) {
return undeclared, nil
}
-func buildKernel(target *sys.Target, dir string) {
- // TODO(dvyukov): use separate temp build dir.
- // This will allow to do build for all archs in parallel and
- // won't destroy user's build state.
+func buildKernel(arch *Arch) error {
+ target := arch.target
+ kernelDir := arch.kernelDir
+ buildDir := arch.buildDir
makeArgs := []string{
"ARCH=" + target.KernelArch,
"CROSS_COMPILE=" + target.CCompilerPrefix,
"CFLAGS=" + strings.Join(target.CrossCFlags, " "),
+ "O=" + buildDir,
}
- out, err := osutil.RunCmd(time.Hour, dir, "make", append(makeArgs, "defconfig")...)
+ out, err := osutil.RunCmd(time.Hour, kernelDir, "make", append(makeArgs, "defconfig")...)
if err != nil {
- failf("make defconfig failed: %v\n%s\n", err, out)
+ return fmt.Errorf("make defconfig failed: %v\n%s\n", err, out)
}
// Without CONFIG_NETFILTER kernel does not build.
- out, err = osutil.RunCmd(time.Minute, dir, "sed", "-i",
+ out, err = osutil.RunCmd(time.Minute, buildDir, "sed", "-i",
"s@# CONFIG_NETFILTER is not set@CONFIG_NETFILTER=y@g", ".config")
if err != nil {
- failf("sed .config failed: %v\n%s\n", err, out)
+ return fmt.Errorf("sed .config failed: %v\n%s\n", err, out)
}
- out, err = osutil.RunCmd(time.Hour, dir, "make", append(makeArgs, "olddefconfig")...)
+ out, err = osutil.RunCmd(time.Hour, kernelDir, "make", append(makeArgs, "olddefconfig")...)
if err != nil {
- failf("make olddefconfig failed: %v\n%s\n", err, out)
+ return fmt.Errorf("make olddefconfig failed: %v\n%s\n", err, out)
}
- out, err = osutil.RunCmd(time.Hour, dir, "make", append(makeArgs, "init/main.o")...)
+ out, err = osutil.RunCmd(time.Hour, kernelDir, "make", append(makeArgs, "init/main.o")...)
if err != nil {
- failf("make failed: %v\n%s\n", err, out)
+ return fmt.Errorf("make failed: %v\n%s\n", err, out)
}
-}
-
-func failf(msg string, args ...interface{}) {
- fmt.Fprintf(os.Stderr, msg+"\n", args...)
- os.Exit(1)
+ return nil
}
diff --git a/sys/syz-extract/fetch.go b/sys/syz-extract/fetch.go
index 25f3f2f47..5def165b5 100644
--- a/sys/syz-extract/fetch.go
+++ b/sys/syz-extract/fetch.go
@@ -18,9 +18,10 @@ import (
// fetchValues converts literal constants (e.g. O_APPEND) or any other C expressions
// into their respective numeric values. It does so by builting and executing a C program
// that prints values of the provided expressions.
-func fetchValues(target *sys.Target, vals, includes, incdirs []string,
- defines map[string]string) (map[string]uint64, map[string]bool, error) {
- bin, out, err := runCompiler(target, nil, includes, incdirs, nil, nil)
+func fetchValues(target *sys.Target, kernelDir, buildDir string,
+ vals, includes, incdirs []string, defines map[string]string) (
+ map[string]uint64, map[string]bool, error) {
+ bin, out, err := runCompiler(target, kernelDir, buildDir, nil, includes, incdirs, nil, nil)
if err != nil {
return nil, nil, fmt.Errorf("failed to run gcc: %v\n%v", err, string(out))
}
@@ -32,7 +33,7 @@ func fetchValues(target *sys.Target, vals, includes, incdirs []string,
}
undeclared := make(map[string]bool)
- bin, out, err = runCompiler(target, vals, includes, incdirs, defines, undeclared)
+ bin, out, err = runCompiler(target, kernelDir, buildDir, vals, includes, incdirs, defines, undeclared)
if err != nil {
for _, errMsg := range []string{
"error: ‘([a-zA-Z0-9_]+)’ undeclared",
@@ -47,7 +48,7 @@ func fetchValues(target *sys.Target, vals, includes, incdirs []string,
}
}
}
- bin, out, err = runCompiler(target, vals, includes, incdirs, defines, undeclared)
+ bin, out, err = runCompiler(target, kernelDir, buildDir, vals, includes, incdirs, defines, undeclared)
if err != nil {
return nil, nil, fmt.Errorf("failed to run gcc: %v\n%v", err, string(out))
}
@@ -86,7 +87,7 @@ func fetchValues(target *sys.Target, vals, includes, incdirs []string,
return res, undeclared, nil
}
-func runCompiler(target *sys.Target, vals, includes, incdirs []string, defines map[string]string, undeclared map[string]bool) (bin string, out []byte, err error) {
+func runCompiler(target *sys.Target, kernelDir, buildDir string, vals, includes, incdirs []string, defines map[string]string, undeclared map[string]bool) (bin string, out []byte, err error) {
includeText := ""
for _, inc := range includes {
includeText += fmt.Sprintf("#include <%v>\n", inc)
@@ -126,20 +127,20 @@ func runCompiler(target *sys.Target, vals, includes, incdirs []string, defines m
"-I.",
"-D__KERNEL__",
"-DKBUILD_MODNAME=\"-\"",
- "-I" + *flagLinux + "/arch/" + arch + "/include",
- "-I" + *flagLinuxBld + "/arch/" + arch + "/include/generated/uapi",
- "-I" + *flagLinuxBld + "/arch/" + arch + "/include/generated",
- "-I" + *flagLinuxBld + "/include",
- "-I" + *flagLinux + "/include",
- "-I" + *flagLinux + "/arch/" + arch + "/include/uapi",
- "-I" + *flagLinuxBld + "/arch/" + arch + "/include/generated/uapi",
- "-I" + *flagLinux + "/include/uapi",
- "-I" + *flagLinuxBld + "/include/generated/uapi",
- "-I" + *flagLinux,
- "-include", *flagLinux + "/include/linux/kconfig.h",
+ "-I" + kernelDir + "/arch/" + arch + "/include",
+ "-I" + buildDir + "/arch/" + arch + "/include/generated/uapi",
+ "-I" + buildDir + "/arch/" + arch + "/include/generated",
+ "-I" + buildDir + "/include",
+ "-I" + kernelDir + "/include",
+ "-I" + kernelDir + "/arch/" + arch + "/include/uapi",
+ "-I" + buildDir + "/arch/" + arch + "/include/generated/uapi",
+ "-I" + kernelDir + "/include/uapi",
+ "-I" + buildDir + "/include/generated/uapi",
+ "-I" + kernelDir,
+ "-include", kernelDir + "/include/linux/kconfig.h",
}...)
for _, incdir := range incdirs {
- args = append(args, "-I"+*flagLinux+"/"+incdir)
+ args = append(args, "-I"+kernelDir+"/"+incdir)
}
cmd := exec.Command("gcc", args...)
cmd.Stdin = strings.NewReader(src)