From 94ef62054cd1f18fb4deaff776695f5a1670a358 Mon Sep 17 00:00:00 2001 From: Dmitry Vyukov Date: Thu, 21 Jun 2018 14:53:21 +0200 Subject: pkg/build: move from pkg/kernel Rename pkg/kernel to pkg/build and prepare for multi-OS support. --- pkg/bisect/bisect.go | 8 +-- pkg/build/build.go | 5 ++ pkg/build/build_test.go | 30 ++++++++ pkg/build/linux.go | 162 ++++++++++++++++++++++++++++++++++++++++++ pkg/build/linux_generated.go | 113 +++++++++++++++++++++++++++++ pkg/instance/instance.go | 6 +- pkg/kernel/generated.go | 113 ----------------------------- pkg/kernel/kernel.go | 164 ------------------------------------------- pkg/kernel/kernel_test.go | 30 -------- 9 files changed, 317 insertions(+), 314 deletions(-) create mode 100644 pkg/build/build.go create mode 100644 pkg/build/build_test.go create mode 100644 pkg/build/linux.go create mode 100644 pkg/build/linux_generated.go delete mode 100644 pkg/kernel/generated.go delete mode 100644 pkg/kernel/kernel.go delete mode 100644 pkg/kernel/kernel_test.go (limited to 'pkg') diff --git a/pkg/bisect/bisect.go b/pkg/bisect/bisect.go index f2770d6ce..4e2ee143f 100644 --- a/pkg/bisect/bisect.go +++ b/pkg/bisect/bisect.go @@ -9,9 +9,9 @@ import ( "path/filepath" "time" + "github.com/google/syzkaller/pkg/build" "github.com/google/syzkaller/pkg/git" "github.com/google/syzkaller/pkg/instance" - "github.com/google/syzkaller/pkg/kernel" "github.com/google/syzkaller/pkg/osutil" "github.com/google/syzkaller/syz-manager/mgrconfig" ) @@ -101,7 +101,7 @@ func (env *env) bisect() (*git.Commit, error) { if env.head, err = git.Poll(cfg.Manager.KernelSrc, cfg.Kernel.Repo, cfg.Kernel.Branch); err != nil { return nil, err } - if err := kernel.Clean(cfg.Manager.KernelSrc); err != nil { + if err := build.Clean(cfg.Manager.KernelSrc); err != nil { return nil, fmt.Errorf("kernel clean failed: %v", err) } env.log("building syzkaller on %v", cfg.Syzkaller.Commit) @@ -214,13 +214,13 @@ func (env *env) test() (git.BisectResult, error) { if err != nil { return 0, err } - compilerID, err := kernel.CompilerIdentity(be.compiler) + compilerID, err := build.CompilerIdentity(be.compiler) if err != nil { return 0, err } env.log("testing commit %v with %v", current.Hash, compilerID) buildStart := time.Now() - if err := kernel.Clean(cfg.Manager.KernelSrc); err != nil { + if err := build.Clean(cfg.Manager.KernelSrc); err != nil { return 0, fmt.Errorf("kernel clean failed: %v", err) } err = env.inst.BuildKernel(be.compiler, cfg.Kernel.Userspace, diff --git a/pkg/build/build.go b/pkg/build/build.go new file mode 100644 index 000000000..b53391ea3 --- /dev/null +++ b/pkg/build/build.go @@ -0,0 +1,5 @@ +// 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. + +// Package build contains helper functions for building kernels/images. +package build diff --git a/pkg/build/build_test.go b/pkg/build/build_test.go new file mode 100644 index 000000000..40422b42a --- /dev/null +++ b/pkg/build/build_test.go @@ -0,0 +1,30 @@ +// Copyright 2017 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 build + +import ( + "os/exec" + "strings" + "testing" +) + +func TestCompilerIdentity(t *testing.T) { + compiler := "gcc" + if _, err := exec.LookPath(compiler); err != nil { + t.Skipf("compiler '%v' is not found: %v", compiler, err) + } + id, err := CompilerIdentity(compiler) + if err != nil { + t.Fatalf("failed: %v", err) + } + if len(id) == 0 { + t.Fatalf("identity is empty") + } + if strings.Contains(id, "\n") { + t.Fatalf("identity contains a new line") + } + // We don't know what's the right answer, + // so just print it for manual inspection. + t.Logf("id: '%v'", id) +} diff --git a/pkg/build/linux.go b/pkg/build/linux.go new file mode 100644 index 000000000..e8c2d0f6d --- /dev/null +++ b/pkg/build/linux.go @@ -0,0 +1,162 @@ +// Copyright 2017 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. + +//go:generate bash -c "echo -en '// AUTOGENERATED FILE\n\n' > linux_generated.go" +//go:generate bash -c "echo -en 'package build\n\n' >> linux_generated.go" +//go:generate bash -c "echo -en 'const createImageScript = `#!/bin/bash\n' >> linux_generated.go" +//go:generate bash -c "cat ../../tools/create-gce-image.sh | grep -v '#' >> linux_generated.go" +//go:generate bash -c "echo -en '`\n\n' >> linux_generated.go" + +package build + +import ( + "bytes" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "runtime" + "strconv" + "strings" + "time" + + "github.com/google/syzkaller/pkg/osutil" +) + +func Build(dir, compiler string, config []byte) error { + configFile := filepath.Join(dir, ".config") + if err := osutil.WriteFile(configFile, config); err != nil { + return fmt.Errorf("failed to write config file: %v", err) + } + if err := osutil.SandboxChown(configFile); err != nil { + return err + } + // One would expect olddefconfig here, but olddefconfig is not present in v3.6 and below. + // oldconfig is the same as olddefconfig if stdin is not set. + // Note: passing in compiler is important since 4.17 (at the very least it's noted in the config). + cmd := osutil.Command("make", "oldconfig", "CC="+compiler) + if err := osutil.Sandbox(cmd, true, true); err != nil { + return err + } + cmd.Dir = dir + if _, err := osutil.Run(10*time.Minute, cmd); err != nil { + return err + } + // We build only bzImage as we currently don't use modules. + cpu := strconv.Itoa(runtime.NumCPU()) + cmd = osutil.Command("make", "bzImage", "-j", cpu, "CC="+compiler) + if err := osutil.Sandbox(cmd, true, true); err != nil { + return err + } + cmd.Dir = dir + // Build of a large kernel can take a while on a 1 CPU VM. + if _, err := osutil.Run(3*time.Hour, cmd); err != nil { + return extractRootCause(err) + } + return nil +} + +func Clean(dir string) error { + cpu := strconv.Itoa(runtime.NumCPU()) + cmd := osutil.Command("make", "distclean", "-j", cpu) + if err := osutil.Sandbox(cmd, true, true); err != nil { + return err + } + cmd.Dir = dir + _, err := osutil.Run(10*time.Minute, cmd) + return err +} + +// CreateImage creates a disk image that is suitable for syzkaller. +// Kernel is taken from kernelDir, userspace system is taken from userspaceDir. +// If cmdlineFile is not empty, contents of the file are appended to the kernel command line. +// If sysctlFile is not empty, contents of the file are appended to the image /etc/sysctl.conf. +// Produces image and root ssh key in the specified files. +func CreateImage(targetOS, targetArch, vmType, kernelDir, userspaceDir, cmdlineFile, sysctlFile, + image, sshkey string) error { + if targetOS != "linux" || targetArch != "amd64" { + return fmt.Errorf("only linux/amd64 is supported") + } + if vmType != "qemu" && vmType != "gce" { + return fmt.Errorf("images can be built only for qemu/gce machines") + } + tempDir, err := ioutil.TempDir("", "syz-build") + if err != nil { + return err + } + defer os.RemoveAll(tempDir) + scriptFile := filepath.Join(tempDir, "create.sh") + if err := osutil.WriteExecFile(scriptFile, []byte(createImageScript)); err != nil { + return fmt.Errorf("failed to write script file: %v", err) + } + bzImage := filepath.Join(kernelDir, filepath.FromSlash("arch/x86/boot/bzImage")) + cmd := osutil.Command(scriptFile, userspaceDir, bzImage) + cmd.Dir = tempDir + cmd.Env = append([]string{}, os.Environ()...) + cmd.Env = append(cmd.Env, + "SYZ_VM_TYPE="+vmType, + "SYZ_CMDLINE_FILE="+osutil.Abs(cmdlineFile), + "SYZ_SYSCTL_FILE="+osutil.Abs(sysctlFile), + ) + if _, err = osutil.Run(time.Hour, cmd); err != nil { + return fmt.Errorf("image build failed: %v", err) + } + // Note: we use CopyFile instead of Rename because src and dst can be on different filesystems. + if err := osutil.CopyFile(filepath.Join(tempDir, "disk.raw"), image); err != nil { + return err + } + if err := osutil.CopyFile(filepath.Join(tempDir, "key"), sshkey); err != nil { + return err + } + if err := os.Chmod(sshkey, 0600); err != nil { + return err + } + return nil +} + +func CompilerIdentity(compiler string) (string, error) { + output, err := osutil.RunCmd(time.Minute, "", compiler, "--version") + if err != nil { + return "", err + } + if len(output) == 0 { + return "", fmt.Errorf("no output from compiler --version") + } + return strings.Split(string(output), "\n")[0], nil +} + +func extractRootCause(err error) error { + verr, ok := err.(*osutil.VerboseError) + if !ok { + return err + } + var cause []byte + for _, line := range bytes.Split(verr.Output, []byte{'\n'}) { + for _, pattern := range buildFailureCauses { + if pattern.weak && cause != nil { + continue + } + if bytes.Contains(line, pattern.pattern) { + cause = line + break + } + } + } + if cause != nil { + verr.Title = string(cause) + } + return verr +} + +type buildFailureCause struct { + pattern []byte + weak bool +} + +var buildFailureCauses = [...]buildFailureCause{ + {pattern: []byte(": error: ")}, + {pattern: []byte(": fatal error: ")}, + {pattern: []byte(": undefined reference to")}, + {weak: true, pattern: []byte(": final link failed: ")}, + {weak: true, pattern: []byte("collect2: error: ")}, +} diff --git a/pkg/build/linux_generated.go b/pkg/build/linux_generated.go new file mode 100644 index 000000000..95da10aee --- /dev/null +++ b/pkg/build/linux_generated.go @@ -0,0 +1,113 @@ +// AUTOGENERATED FILE + +package build + +const createImageScript = `#!/bin/bash + + +set -eux + +CLEANUP="" +trap 'eval " $CLEANUP"' EXIT + +if [ ! -e $1/sbin/init ]; then + echo "usage: create-gce-image.sh /dir/with/user/space/system /path/to/bzImage" + exit 1 +fi + +if [ "$(basename $2)" != "bzImage" ]; then + echo "usage: create-gce-image.sh /dir/with/user/space/system /path/to/bzImage" + exit 1 +fi + +SYZ_VM_TYPE="${SYZ_VM_TYPE:-qemu}" +if [ "$SYZ_VM_TYPE" == "qemu" ]; then + : +elif [ "$SYZ_VM_TYPE" == "gce" ]; then + : +else + echo "SYZ_VM_TYPE has unsupported value $SYZ_VM_TYPE" + exit 1 +fi + +sudo umount disk.mnt || true +if [ "$SYZ_VM_TYPE" == "qemu" ]; then + : +elif [ "$SYZ_VM_TYPE" == "gce" ]; then + sudo modprobe nbd + sudo qemu-nbd -d /dev/nbd0 || true +fi +rm -rf disk.mnt disk.raw || true + +fallocate -l 2G disk.raw +if [ "$SYZ_VM_TYPE" == "qemu" ]; then + DISKDEV="$(sudo losetup -f --show -P disk.raw)" + CLEANUP="sudo losetup -d $DISKDEV; $CLEANUP" +elif [ "$SYZ_VM_TYPE" == "gce" ]; then + DISKDEV="/dev/nbd0" + sudo qemu-nbd -c $DISKDEV --format=raw disk.raw + CLEANUP="sudo qemu-nbd -d $DISKDEV; $CLEANUP" +fi +echo -en "o\nn\np\n1\n\n\na\nw\n" | sudo fdisk $DISKDEV +PARTDEV=$DISKDEV"p1" +until [ -e $PARTDEV ]; do sleep 1; done +sudo -E mkfs.ext4 $PARTDEV +mkdir -p disk.mnt +CLEANUP="rm -rf disk.mnt; $CLEANUP" +sudo mount $PARTDEV disk.mnt +CLEANUP="sudo umount disk.mnt; $CLEANUP" +sudo cp -a $1/. disk.mnt/. +sudo cp $2 disk.mnt/vmlinuz +sudo sed -i "/^root/ { s/:x:/::/ }" disk.mnt/etc/passwd +echo "T0:23:respawn:/sbin/getty -L ttyS0 115200 vt100" | sudo tee -a disk.mnt/etc/inittab +echo -en "auto lo\niface lo inet loopback\nauto eth0\niface eth0 inet dhcp\n" | sudo tee disk.mnt/etc/network/interfaces +echo "debugfs /sys/kernel/debug debugfs defaults 0 0" | sudo tee -a disk.mnt/etc/fstab +echo 'binfmt_misc /proc/sys/fs/binfmt_misc binfmt_misc defaults 0 0' | sudo tee -a disk.mnt/etc/fstab +for i in {0..31}; do + echo "KERNEL==\"binder$i\", NAME=\"binder$i\", MODE=\"0666\"" | \ + sudo tee -a disk.mnt/etc/udev/50-binder.rules +done +echo 'SELINUX=disabled' | sudo tee disk.mnt/etc/selinux/config + +echo "kernel.printk = 7 4 1 3" | sudo tee -a disk.mnt/etc/sysctl.conf +echo "debug.exception-trace = 0" | sudo tee -a disk.mnt/etc/sysctl.conf +SYZ_SYSCTL_FILE="${SYZ_SYSCTL_FILE:-}" +if [ "$SYZ_SYSCTL_FILE" != "" ]; then + cat $SYZ_SYSCTL_FILE | sudo tee -a disk.mnt/etc/sysctl.conf +fi + +echo -en "127.0.0.1\tlocalhost\n" | sudo tee disk.mnt/etc/hosts +echo "nameserver 8.8.8.8" | sudo tee -a disk.mnt/etc/resolve.conf +echo "ClientAliveInterval 420" | sudo tee -a disk.mnt/etc/ssh/sshd_config +echo "syzkaller" | sudo tee disk.mnt/etc/hostname +rm -f key key.pub +ssh-keygen -f key -t rsa -N "" +sudo mkdir -p disk.mnt/root/.ssh +sudo cp key.pub disk.mnt/root/.ssh/authorized_keys +sudo chown root disk.mnt/root/.ssh/authorized_keys +sudo mkdir -p disk.mnt/boot/grub + +CMDLINE="" +SYZ_CMDLINE_FILE="${SYZ_CMDLINE_FILE:-}" +if [ "$SYZ_CMDLINE_FILE" != "" ]; then + CMDLINE=$(awk '{printf("%s ", $0)}' $SYZ_CMDLINE_FILE) +fi + +cat << EOF | sudo tee disk.mnt/boot/grub/grub.cfg +terminal_input console +terminal_output console +set timeout=0 +menuentry 'linux' --class gnu-linux --class gnu --class os { + insmod vbe + insmod vga + insmod video_bochs + insmod video_cirrus + insmod gzio + insmod part_msdos + insmod ext2 + set root='(hd0,1)' + linux /vmlinuz root=/dev/sda1 console=ttyS0 earlyprintk=serial vsyscall=native rodata=n ftrace_dump_on_oops=orig_cpu oops=panic panic_on_warn=1 nmi_watchdog=panic panic=86400 $CMDLINE +} +EOF +sudo grub-install --target=i386-pc --boot-directory=disk.mnt/boot --no-floppy $DISKDEV +` diff --git a/pkg/instance/instance.go b/pkg/instance/instance.go index e7ab3c9ec..2294fed9b 100644 --- a/pkg/instance/instance.go +++ b/pkg/instance/instance.go @@ -14,9 +14,9 @@ import ( "strings" "time" + "github.com/google/syzkaller/pkg/build" "github.com/google/syzkaller/pkg/csource" "github.com/google/syzkaller/pkg/git" - "github.com/google/syzkaller/pkg/kernel" "github.com/google/syzkaller/pkg/log" "github.com/google/syzkaller/pkg/osutil" "github.com/google/syzkaller/pkg/report" @@ -79,13 +79,13 @@ func (env *Env) BuildSyzkaller(repo, commit string) error { func (env *Env) BuildKernel(compilerBin, userspaceDir, cmdlineFile, sysctlFile string, kernelConfig []byte) error { cfg := env.cfg - if err := kernel.Build(cfg.KernelSrc, compilerBin, kernelConfig); err != nil { + if err := build.Build(cfg.KernelSrc, compilerBin, kernelConfig); err != nil { return osutil.PrependContext("kernel build failed", err) } cfg.KernelObj = cfg.KernelSrc cfg.Image = filepath.Join(cfg.Workdir, "syz-image") cfg.SSHKey = filepath.Join(cfg.Workdir, "syz-key") - if err := kernel.CreateImage(cfg.TargetOS, cfg.TargetVMArch, cfg.Type, + if err := build.CreateImage(cfg.TargetOS, cfg.TargetVMArch, cfg.Type, cfg.KernelSrc, userspaceDir, cmdlineFile, sysctlFile, cfg.Image, cfg.SSHKey); err != nil { return osutil.PrependContext("image build failed", err) } diff --git a/pkg/kernel/generated.go b/pkg/kernel/generated.go deleted file mode 100644 index 9c8fe51e8..000000000 --- a/pkg/kernel/generated.go +++ /dev/null @@ -1,113 +0,0 @@ -// AUTOGENERATED FILE - -package kernel - -const createImageScript = `#!/bin/bash - - -set -eux - -CLEANUP="" -trap 'eval " $CLEANUP"' EXIT - -if [ ! -e $1/sbin/init ]; then - echo "usage: create-gce-image.sh /dir/with/user/space/system /path/to/bzImage" - exit 1 -fi - -if [ "$(basename $2)" != "bzImage" ]; then - echo "usage: create-gce-image.sh /dir/with/user/space/system /path/to/bzImage" - exit 1 -fi - -SYZ_VM_TYPE="${SYZ_VM_TYPE:-qemu}" -if [ "$SYZ_VM_TYPE" == "qemu" ]; then - : -elif [ "$SYZ_VM_TYPE" == "gce" ]; then - : -else - echo "SYZ_VM_TYPE has unsupported value $SYZ_VM_TYPE" - exit 1 -fi - -sudo umount disk.mnt || true -if [ "$SYZ_VM_TYPE" == "qemu" ]; then - : -elif [ "$SYZ_VM_TYPE" == "gce" ]; then - sudo modprobe nbd - sudo qemu-nbd -d /dev/nbd0 || true -fi -rm -rf disk.mnt disk.raw || true - -fallocate -l 2G disk.raw -if [ "$SYZ_VM_TYPE" == "qemu" ]; then - DISKDEV="$(sudo losetup -f --show -P disk.raw)" - CLEANUP="sudo losetup -d $DISKDEV; $CLEANUP" -elif [ "$SYZ_VM_TYPE" == "gce" ]; then - DISKDEV="/dev/nbd0" - sudo qemu-nbd -c $DISKDEV --format=raw disk.raw - CLEANUP="sudo qemu-nbd -d $DISKDEV; $CLEANUP" -fi -echo -en "o\nn\np\n1\n\n\na\nw\n" | sudo fdisk $DISKDEV -PARTDEV=$DISKDEV"p1" -until [ -e $PARTDEV ]; do sleep 1; done -sudo -E mkfs.ext4 $PARTDEV -mkdir -p disk.mnt -CLEANUP="rm -rf disk.mnt; $CLEANUP" -sudo mount $PARTDEV disk.mnt -CLEANUP="sudo umount disk.mnt; $CLEANUP" -sudo cp -a $1/. disk.mnt/. -sudo cp $2 disk.mnt/vmlinuz -sudo sed -i "/^root/ { s/:x:/::/ }" disk.mnt/etc/passwd -echo "T0:23:respawn:/sbin/getty -L ttyS0 115200 vt100" | sudo tee -a disk.mnt/etc/inittab -echo -en "auto lo\niface lo inet loopback\nauto eth0\niface eth0 inet dhcp\n" | sudo tee disk.mnt/etc/network/interfaces -echo "debugfs /sys/kernel/debug debugfs defaults 0 0" | sudo tee -a disk.mnt/etc/fstab -echo 'binfmt_misc /proc/sys/fs/binfmt_misc binfmt_misc defaults 0 0' | sudo tee -a disk.mnt/etc/fstab -for i in {0..31}; do - echo "KERNEL==\"binder$i\", NAME=\"binder$i\", MODE=\"0666\"" | \ - sudo tee -a disk.mnt/etc/udev/50-binder.rules -done -echo 'SELINUX=disabled' | sudo tee disk.mnt/etc/selinux/config - -echo "kernel.printk = 7 4 1 3" | sudo tee -a disk.mnt/etc/sysctl.conf -echo "debug.exception-trace = 0" | sudo tee -a disk.mnt/etc/sysctl.conf -SYZ_SYSCTL_FILE="${SYZ_SYSCTL_FILE:-}" -if [ "$SYZ_SYSCTL_FILE" != "" ]; then - cat $SYZ_SYSCTL_FILE | sudo tee -a disk.mnt/etc/sysctl.conf -fi - -echo -en "127.0.0.1\tlocalhost\n" | sudo tee disk.mnt/etc/hosts -echo "nameserver 8.8.8.8" | sudo tee -a disk.mnt/etc/resolve.conf -echo "ClientAliveInterval 420" | sudo tee -a disk.mnt/etc/ssh/sshd_config -echo "syzkaller" | sudo tee disk.mnt/etc/hostname -rm -f key key.pub -ssh-keygen -f key -t rsa -N "" -sudo mkdir -p disk.mnt/root/.ssh -sudo cp key.pub disk.mnt/root/.ssh/authorized_keys -sudo chown root disk.mnt/root/.ssh/authorized_keys -sudo mkdir -p disk.mnt/boot/grub - -CMDLINE="" -SYZ_CMDLINE_FILE="${SYZ_CMDLINE_FILE:-}" -if [ "$SYZ_CMDLINE_FILE" != "" ]; then - CMDLINE=$(awk '{printf("%s ", $0)}' $SYZ_CMDLINE_FILE) -fi - -cat << EOF | sudo tee disk.mnt/boot/grub/grub.cfg -terminal_input console -terminal_output console -set timeout=0 -menuentry 'linux' --class gnu-linux --class gnu --class os { - insmod vbe - insmod vga - insmod video_bochs - insmod video_cirrus - insmod gzio - insmod part_msdos - insmod ext2 - set root='(hd0,1)' - linux /vmlinuz root=/dev/sda1 console=ttyS0 earlyprintk=serial vsyscall=native rodata=n ftrace_dump_on_oops=orig_cpu oops=panic panic_on_warn=1 nmi_watchdog=panic panic=86400 $CMDLINE -} -EOF -sudo grub-install --target=i386-pc --boot-directory=disk.mnt/boot --no-floppy $DISKDEV -` diff --git a/pkg/kernel/kernel.go b/pkg/kernel/kernel.go deleted file mode 100644 index 5bcc0fc42..000000000 --- a/pkg/kernel/kernel.go +++ /dev/null @@ -1,164 +0,0 @@ -// Copyright 2017 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. - -//go:generate bash -c "echo -en '// AUTOGENERATED FILE\n\n' > generated.go" -//go:generate bash -c "echo -en 'package kernel\n\n' >> generated.go" -//go:generate bash -c "echo -en 'const createImageScript = `#!/bin/bash\n' >> generated.go" -//go:generate bash -c "cat ../../tools/create-gce-image.sh | grep -v '#' >> generated.go" -//go:generate bash -c "echo -en '`\n\n' >> generated.go" - -// Package kernel contains helper functions for working with Linux kernel -// (building kernel/image). -package kernel - -import ( - "bytes" - "fmt" - "io/ioutil" - "os" - "path/filepath" - "runtime" - "strconv" - "strings" - "time" - - "github.com/google/syzkaller/pkg/osutil" -) - -func Build(dir, compiler string, config []byte) error { - configFile := filepath.Join(dir, ".config") - if err := osutil.WriteFile(configFile, config); err != nil { - return fmt.Errorf("failed to write config file: %v", err) - } - if err := osutil.SandboxChown(configFile); err != nil { - return err - } - // One would expect olddefconfig here, but olddefconfig is not present in v3.6 and below. - // oldconfig is the same as olddefconfig if stdin is not set. - // Note: passing in compiler is important since 4.17 (at the very least it's noted in the config). - cmd := osutil.Command("make", "oldconfig", "CC="+compiler) - if err := osutil.Sandbox(cmd, true, true); err != nil { - return err - } - cmd.Dir = dir - if _, err := osutil.Run(10*time.Minute, cmd); err != nil { - return err - } - // We build only bzImage as we currently don't use modules. - cpu := strconv.Itoa(runtime.NumCPU()) - cmd = osutil.Command("make", "bzImage", "-j", cpu, "CC="+compiler) - if err := osutil.Sandbox(cmd, true, true); err != nil { - return err - } - cmd.Dir = dir - // Build of a large kernel can take a while on a 1 CPU VM. - if _, err := osutil.Run(3*time.Hour, cmd); err != nil { - return extractRootCause(err) - } - return nil -} - -func Clean(dir string) error { - cpu := strconv.Itoa(runtime.NumCPU()) - cmd := osutil.Command("make", "distclean", "-j", cpu) - if err := osutil.Sandbox(cmd, true, true); err != nil { - return err - } - cmd.Dir = dir - _, err := osutil.Run(10*time.Minute, cmd) - return err -} - -// CreateImage creates a disk image that is suitable for syzkaller. -// Kernel is taken from kernelDir, userspace system is taken from userspaceDir. -// If cmdlineFile is not empty, contents of the file are appended to the kernel command line. -// If sysctlFile is not empty, contents of the file are appended to the image /etc/sysctl.conf. -// Produces image and root ssh key in the specified files. -func CreateImage(targetOS, targetArch, vmType, kernelDir, userspaceDir, cmdlineFile, sysctlFile, - image, sshkey string) error { - if targetOS != "linux" || targetArch != "amd64" { - return fmt.Errorf("only linux/amd64 is supported") - } - if vmType != "qemu" && vmType != "gce" { - return fmt.Errorf("images can be built only for qemu/gce machines") - } - tempDir, err := ioutil.TempDir("", "syz-build") - if err != nil { - return err - } - defer os.RemoveAll(tempDir) - scriptFile := filepath.Join(tempDir, "create.sh") - if err := osutil.WriteExecFile(scriptFile, []byte(createImageScript)); err != nil { - return fmt.Errorf("failed to write script file: %v", err) - } - bzImage := filepath.Join(kernelDir, filepath.FromSlash("arch/x86/boot/bzImage")) - cmd := osutil.Command(scriptFile, userspaceDir, bzImage) - cmd.Dir = tempDir - cmd.Env = append([]string{}, os.Environ()...) - cmd.Env = append(cmd.Env, - "SYZ_VM_TYPE="+vmType, - "SYZ_CMDLINE_FILE="+osutil.Abs(cmdlineFile), - "SYZ_SYSCTL_FILE="+osutil.Abs(sysctlFile), - ) - if _, err = osutil.Run(time.Hour, cmd); err != nil { - return fmt.Errorf("image build failed: %v", err) - } - // Note: we use CopyFile instead of Rename because src and dst can be on different filesystems. - if err := osutil.CopyFile(filepath.Join(tempDir, "disk.raw"), image); err != nil { - return err - } - if err := osutil.CopyFile(filepath.Join(tempDir, "key"), sshkey); err != nil { - return err - } - if err := os.Chmod(sshkey, 0600); err != nil { - return err - } - return nil -} - -func CompilerIdentity(compiler string) (string, error) { - output, err := osutil.RunCmd(time.Minute, "", compiler, "--version") - if err != nil { - return "", err - } - if len(output) == 0 { - return "", fmt.Errorf("no output from compiler --version") - } - return strings.Split(string(output), "\n")[0], nil -} - -func extractRootCause(err error) error { - verr, ok := err.(*osutil.VerboseError) - if !ok { - return err - } - var cause []byte - for _, line := range bytes.Split(verr.Output, []byte{'\n'}) { - for _, pattern := range buildFailureCauses { - if pattern.weak && cause != nil { - continue - } - if bytes.Contains(line, pattern.pattern) { - cause = line - break - } - } - } - if cause != nil { - verr.Title = string(cause) - } - return verr -} - -type buildFailureCause struct { - pattern []byte - weak bool -} - -var buildFailureCauses = [...]buildFailureCause{ - {pattern: []byte(": error: ")}, - {pattern: []byte(": fatal error: ")}, - {pattern: []byte(": undefined reference to")}, - {weak: true, pattern: []byte(": final link failed: ")}, - {weak: true, pattern: []byte("collect2: error: ")}, -} diff --git a/pkg/kernel/kernel_test.go b/pkg/kernel/kernel_test.go deleted file mode 100644 index 18d7936d0..000000000 --- a/pkg/kernel/kernel_test.go +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2017 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 kernel - -import ( - "os/exec" - "strings" - "testing" -) - -func TestCompilerIdentity(t *testing.T) { - compiler := "gcc" - if _, err := exec.LookPath(compiler); err != nil { - t.Skipf("compiler '%v' is not found: %v", compiler, err) - } - id, err := CompilerIdentity(compiler) - if err != nil { - t.Fatalf("failed: %v", err) - } - if len(id) == 0 { - t.Fatalf("identity is empty") - } - if strings.Contains(id, "\n") { - t.Fatalf("identity contains a new line") - } - // We don't know what's the right answer, - // so just print it for manual inspection. - t.Logf("id: '%v'", id) -} -- cgit mrf-deployment