github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/sys/syz-extract/linux.go (about)

     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  package main
     5  
     6  import (
     7  	"fmt"
     8  	"path/filepath"
     9  	"runtime"
    10  	"strings"
    11  	"time"
    12  
    13  	"github.com/google/syzkaller/pkg/build"
    14  	"github.com/google/syzkaller/pkg/compiler"
    15  	"github.com/google/syzkaller/pkg/osutil"
    16  )
    17  
    18  type linux struct{}
    19  
    20  func (*linux) prepare(sourcedir string, build bool, arches []*Arch) error {
    21  	if build {
    22  		// Run 'make mrproper', otherwise out-of-tree build fails.
    23  		// However, it takes unreasonable amount of time,
    24  		// so first check few files and if they are missing hope for best.
    25  		for _, a := range arches {
    26  			arch := a.target.KernelArch
    27  			if osutil.IsExist(filepath.Join(sourcedir, ".config")) ||
    28  				osutil.IsExist(filepath.Join(sourcedir, "init/main.o")) ||
    29  				osutil.IsExist(filepath.Join(sourcedir, "include/config")) ||
    30  				osutil.IsExist(filepath.Join(sourcedir, "include/generated/compile.h")) ||
    31  				osutil.IsExist(filepath.Join(sourcedir, "arch", arch, "include", "generated")) {
    32  				fmt.Printf("make mrproper ARCH=%v\n", arch)
    33  				out, err := osutil.RunCmd(time.Hour, sourcedir, "make", "mrproper", "ARCH="+arch,
    34  					"-j", fmt.Sprint(runtime.NumCPU()))
    35  				if err != nil {
    36  					return fmt.Errorf("make mrproper failed: %w\n%s", err, out)
    37  				}
    38  			}
    39  		}
    40  	} else {
    41  		if len(arches) > 1 {
    42  			return fmt.Errorf("more than 1 arch is invalid without -build")
    43  		}
    44  	}
    45  	return nil
    46  }
    47  
    48  func (*linux) prepareArch(arch *Arch) error {
    49  	// Kernel misses these headers on some arches.
    50  	// So we create empty stubs in buildDir/syzkaller and add -IbuildDir/syzkaller
    51  	// as the last flag so it won't override real kernel headers.
    52  	for hdr, data := range map[string]string{
    53  		// This is the only compiler header kernel uses,
    54  		// need to provide it since we use -nostdinc below.
    55  		"stdarg.h": `
    56  #pragma once
    57  #define va_list __builtin_va_list
    58  #define va_start __builtin_va_start
    59  #define va_end __builtin_va_end
    60  #define va_arg __builtin_va_arg
    61  #define va_copy __builtin_va_copy
    62  #define __va_copy __builtin_va_copy
    63  `,
    64  		"asm/a.out.h":    "",
    65  		"asm/prctl.h":    "",
    66  		"asm/mce.h":      "",
    67  		"asm/msr.h":      "",
    68  		"uapi/asm/msr.h": "",
    69  	} {
    70  		fullPath := filepath.Join(arch.buildDir, "syzkaller", hdr)
    71  		if err := osutil.MkdirAll(filepath.Dir(fullPath)); err != nil {
    72  			return err
    73  		}
    74  		if err := osutil.WriteFile(fullPath, []byte(data)); err != nil {
    75  			return nil
    76  		}
    77  	}
    78  	if !arch.build {
    79  		return nil
    80  	}
    81  	kernelDir := arch.sourceDir
    82  	makeArgs := build.LinuxMakeArgs(arch.target, "", "", "", arch.buildDir)
    83  	out, err := osutil.RunCmd(time.Hour, kernelDir, "make", append(makeArgs, "defconfig")...)
    84  	if err != nil {
    85  		return fmt.Errorf("make defconfig failed: %w\n%s", err, out)
    86  	}
    87  	_, err = osutil.RunCmd(time.Minute, arch.buildDir, filepath.Join(kernelDir, "scripts", "config"),
    88  		// powerpc arch is configured to be big-endian by default, but we want little-endian powerpc.
    89  		// Since all of our archs are little-endian for now, we just blindly switch it.
    90  		"-d", "CPU_BIG_ENDIAN", "-e", "CPU_LITTLE_ENDIAN",
    91  		// s390 enables BTF in defconfig, but our packaged toolchains can't build it.
    92  		"-d", "DEBUG_INFO_BTF",
    93  		// Without CONFIG_NETFILTER kernel does not build.
    94  		"-e", "NETFILTER",
    95  		// include/net/mptcp.h is the only header in kernel that guards some
    96  		// of the consts with own config, so we need to enable CONFIG_MPTCP.
    97  		"-e", "MPTCP",
    98  		// security/smack/smack.h requires this to build.
    99  		"-e", "SECURITY",
   100  		"-e", "SECURITY_SMACK",
   101  		// include/net/nl802154.h does not define some consts without this.
   102  		"-e", "IEEE802154", "-e", "IEEE802154_NL802154_EXPERIMENTAL",
   103  	)
   104  	if err != nil {
   105  		return err
   106  	}
   107  	out, err = osutil.RunCmd(time.Hour, kernelDir, "make", append(makeArgs, "olddefconfig")...)
   108  	if err != nil {
   109  		return fmt.Errorf("make olddefconfig failed: %w\n%s", err, out)
   110  	}
   111  	out, err = osutil.RunCmd(time.Hour, kernelDir, "make", append(makeArgs, "init/main.o")...)
   112  	if err != nil {
   113  		return fmt.Errorf("make failed: %w\n%s", err, out)
   114  	}
   115  	return nil
   116  }
   117  
   118  // nolint: goconst
   119  func (*linux) processFile(arch *Arch, info *compiler.ConstInfo) (map[string]uint64, map[string]bool, error) {
   120  	headerArch := arch.target.KernelHeaderArch
   121  	sourceDir := arch.sourceDir
   122  	buildDir := arch.buildDir
   123  	args := []string{
   124  		// This makes the build completely hermetic, only kernel headers are used.
   125  		"-nostdinc",
   126  		"-w", "-fmessage-length=0",
   127  		"-O3", // required to get expected values for some __builtin_constant_p
   128  		"-I.",
   129  		"-D__KERNEL__",
   130  		"-DKBUILD_MODNAME=\"-\"",
   131  		"-I" + sourceDir + "/arch/" + headerArch + "/include",
   132  		"-I" + buildDir + "/arch/" + headerArch + "/include/generated/uapi",
   133  		"-I" + buildDir + "/arch/" + headerArch + "/include/generated",
   134  		"-I" + sourceDir + "/arch/" + headerArch + "/include/asm/mach-malta",
   135  		"-I" + sourceDir + "/arch/" + headerArch + "/include/asm/mach-generic",
   136  		"-I" + buildDir + "/include",
   137  		"-I" + sourceDir + "/include",
   138  		"-I" + sourceDir + "/arch/" + headerArch + "/include/uapi",
   139  		"-I" + sourceDir + "/include/uapi",
   140  		"-I" + buildDir + "/include/generated/uapi",
   141  		"-I" + sourceDir,
   142  		"-I" + sourceDir + "/include/linux",
   143  		"-I" + buildDir + "/syzkaller",
   144  		"-include", sourceDir + "/include/linux/kconfig.h",
   145  	}
   146  	args = append(args, arch.target.CFlags...)
   147  	for _, incdir := range info.Incdirs {
   148  		args = append(args, "-I"+sourceDir+"/"+incdir)
   149  	}
   150  	if arch.includeDirs != "" {
   151  		for _, dir := range strings.Split(arch.includeDirs, ",") {
   152  			args = append(args, "-I"+dir)
   153  		}
   154  	}
   155  	params := &extractParams{
   156  		AddSource:      "#include <asm/unistd.h>",
   157  		ExtractFromELF: true,
   158  		TargetEndian:   arch.target.HostEndian,
   159  	}
   160  	cc := arch.target.CCompiler
   161  	res, undeclared, err := extract(info, cc, args, params)
   162  	if err != nil {
   163  		return nil, nil, err
   164  	}
   165  	if arch.target.PtrSize == 4 {
   166  		// mmap syscall on i386/arm is translated to old_mmap and has different signature.
   167  		// As a workaround fix it up to mmap2, which has signature that we expect.
   168  		// pkg/csource has the same hack.
   169  		const mmap = "__NR_mmap"
   170  		const mmap2 = "__NR_mmap2"
   171  		if res[mmap] != 0 || undeclared[mmap] {
   172  			if res[mmap2] == 0 {
   173  				return nil, nil, fmt.Errorf("%v is missing", mmap2)
   174  			}
   175  			res[mmap] = res[mmap2]
   176  			delete(undeclared, mmap)
   177  		}
   178  	}
   179  	return res, undeclared, nil
   180  }