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.
This commit is contained in:
Dmitry Vyukov 2017-09-14 15:25:06 +02:00
parent 7296cf374d
commit 4503776d2b
2 changed files with 138 additions and 69 deletions

View File

@ -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
}
files := make([]File, n)
inc := make(chan *File, n)
for i, f := range flag.Args() {
files[i].name = f
inc <- &files[i]
target := sys.Targets[OS][archStr]
if target == nil {
failf("unknown arch: %v", archStr)
}
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)
arch := &Arch{
target: target,
kernelDir: *flagLinux,
buildDir: buildDir,
build: *flagBuild,
}
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
}

View File

@ -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)