• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2017 syzkaller project authors. All rights reserved.
2// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
3
4//go:generate bash -c "echo -en '// AUTOGENERATED FILE\n\n' > linux_generated.go"
5//go:generate bash -c "echo -en 'package build\n\n' >> linux_generated.go"
6//go:generate bash -c "echo -en 'const createImageScript = `#!/bin/bash\n' >> linux_generated.go"
7//go:generate bash -c "cat ../../tools/create-gce-image.sh | grep -v '#' >> linux_generated.go"
8//go:generate bash -c "echo -en '`\n\n' >> linux_generated.go"
9
10package build
11
12import (
13	"bytes"
14	"fmt"
15	"io/ioutil"
16	"os"
17	"path/filepath"
18	"runtime"
19	"strconv"
20	"time"
21
22	"github.com/google/syzkaller/pkg/osutil"
23)
24
25type linux struct{}
26
27func (linux linux) build(targetArch, vmType, kernelDir, outputDir, compiler, userspaceDir,
28	cmdlineFile, sysctlFile string, config []byte) error {
29	if err := linux.buildKernel(kernelDir, outputDir, compiler, config); err != nil {
30		return err
31	}
32	if err := linux.createImage(vmType, kernelDir, outputDir, userspaceDir, cmdlineFile, sysctlFile); err != nil {
33		return err
34	}
35	return nil
36}
37
38func (linux) buildKernel(kernelDir, outputDir, compiler string, config []byte) error {
39	configFile := filepath.Join(kernelDir, ".config")
40	if err := osutil.WriteFile(configFile, config); err != nil {
41		return fmt.Errorf("failed to write config file: %v", err)
42	}
43	if err := osutil.SandboxChown(configFile); err != nil {
44		return err
45	}
46	// One would expect olddefconfig here, but olddefconfig is not present in v3.6 and below.
47	// oldconfig is the same as olddefconfig if stdin is not set.
48	// Note: passing in compiler is important since 4.17 (at the very least it's noted in the config).
49	cmd := osutil.Command("make", "oldconfig", "CC="+compiler)
50	if err := osutil.Sandbox(cmd, true, true); err != nil {
51		return err
52	}
53	cmd.Dir = kernelDir
54	if _, err := osutil.Run(10*time.Minute, cmd); err != nil {
55		return err
56	}
57	// Write updated kernel config early, so that it's captured on build failures.
58	outputConfig := filepath.Join(outputDir, "kernel.config")
59	if err := osutil.CopyFile(configFile, outputConfig); err != nil {
60		return err
61	}
62	// We build only bzImage as we currently don't use modules.
63	cpu := strconv.Itoa(runtime.NumCPU())
64	cmd = osutil.Command("make", "bzImage", "-j", cpu, "CC="+compiler)
65	if err := osutil.Sandbox(cmd, true, true); err != nil {
66		return err
67	}
68	cmd.Dir = kernelDir
69	if _, err := osutil.Run(time.Hour, cmd); err != nil {
70		return extractRootCause(err)
71	}
72	vmlinux := filepath.Join(kernelDir, "vmlinux")
73	outputVmlinux := filepath.Join(outputDir, "obj", "vmlinux")
74	if err := os.Rename(vmlinux, outputVmlinux); err != nil {
75		return fmt.Errorf("failed to rename vmlinux: %v", err)
76	}
77	return nil
78}
79
80func (linux) createImage(vmType, kernelDir, outputDir, userspaceDir, cmdlineFile, sysctlFile string) error {
81	tempDir, err := ioutil.TempDir("", "syz-build")
82	if err != nil {
83		return err
84	}
85	defer os.RemoveAll(tempDir)
86	scriptFile := filepath.Join(tempDir, "create.sh")
87	if err := osutil.WriteExecFile(scriptFile, []byte(createImageScript)); err != nil {
88		return fmt.Errorf("failed to write script file: %v", err)
89	}
90	bzImage := filepath.Join(kernelDir, filepath.FromSlash("arch/x86/boot/bzImage"))
91	cmd := osutil.Command(scriptFile, userspaceDir, bzImage)
92	cmd.Dir = tempDir
93	cmd.Env = append([]string{}, os.Environ()...)
94	cmd.Env = append(cmd.Env,
95		"SYZ_VM_TYPE="+vmType,
96		"SYZ_CMDLINE_FILE="+osutil.Abs(cmdlineFile),
97		"SYZ_SYSCTL_FILE="+osutil.Abs(sysctlFile),
98	)
99	if _, err = osutil.Run(time.Hour, cmd); err != nil {
100		return fmt.Errorf("image build failed: %v", err)
101	}
102	// Note: we use CopyFile instead of Rename because src and dst can be on different filesystems.
103	imageFile := filepath.Join(outputDir, "image")
104	if err := osutil.CopyFile(filepath.Join(tempDir, "disk.raw"), imageFile); err != nil {
105		return err
106	}
107	keyFile := filepath.Join(outputDir, "key")
108	if err := osutil.CopyFile(filepath.Join(tempDir, "key"), keyFile); err != nil {
109		return err
110	}
111	if err := os.Chmod(keyFile, 0600); err != nil {
112		return err
113	}
114	return nil
115}
116
117func (linux) clean(kernelDir string) error {
118	cpu := strconv.Itoa(runtime.NumCPU())
119	cmd := osutil.Command("make", "distclean", "-j", cpu)
120	if err := osutil.Sandbox(cmd, true, true); err != nil {
121		return err
122	}
123	cmd.Dir = kernelDir
124	_, err := osutil.Run(10*time.Minute, cmd)
125	return err
126}
127
128func extractRootCause(err error) error {
129	verr, ok := err.(*osutil.VerboseError)
130	if !ok {
131		return err
132	}
133	var cause []byte
134	for _, line := range bytes.Split(verr.Output, []byte{'\n'}) {
135		for _, pattern := range buildFailureCauses {
136			if pattern.weak && cause != nil {
137				continue
138			}
139			if bytes.Contains(line, pattern.pattern) {
140				cause = line
141				break
142			}
143		}
144	}
145	if cause != nil {
146		verr.Title = string(cause)
147	}
148	return KernelBuildError{verr}
149}
150
151type buildFailureCause struct {
152	pattern []byte
153	weak    bool
154}
155
156var buildFailureCauses = [...]buildFailureCause{
157	{pattern: []byte(": error: ")},
158	{pattern: []byte(": fatal error: ")},
159	{pattern: []byte(": undefined reference to")},
160	{weak: true, pattern: []byte(": final link failed: ")},
161	{weak: true, pattern: []byte("collect2: error: ")},
162}
163