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