code-intelligence.com/cifuzz@v0.40.0/internal/build/build.go (about)

     1  package build
     2  
     3  import (
     4  	"os"
     5  	"runtime"
     6  
     7  	"github.com/Masterminds/semver"
     8  
     9  	"code-intelligence.com/cifuzz/util/envutil"
    10  )
    11  
    12  type Result struct {
    13  	// A name which uniquely identifies the fuzz test and is a valid path
    14  	Name string
    15  	// Optional name for the fuzz test target method
    16  	TargetMethod string
    17  	// Canonical path of the fuzz test executable
    18  	Executable string
    19  	// Canonical path of the fuzz test's generated corpus directory
    20  	GeneratedCorpus string
    21  	// Canonical path of the fuzz test's default seed corpus directory
    22  	SeedCorpus string
    23  	// Canonical path of the build directory
    24  	BuildDir string
    25  	// The sanitizers with which the fuzz test was built
    26  	Sanitizers []string
    27  	// The canonical paths of the fuzz test's runtime dependencies
    28  	RuntimeDeps []string
    29  	// Canonical path of the directory to which source file paths should
    30  	// be made relative
    31  	ProjectDir string
    32  }
    33  
    34  func CommonBuildEnv() ([]string, error) {
    35  	var err error
    36  	env := os.Environ()
    37  
    38  	// Set CIFUZZ=1 to allow the build system to figure out that it was
    39  	// started by cifuzz.
    40  	env, err = envutil.Setenv(env, "CIFUZZ", "1")
    41  	if err != nil {
    42  		return nil, err
    43  	}
    44  
    45  	// On Windows, our preferred compiler is clang-cl, which can't easily be run
    46  	// from an arbitrary terminal as it requires about a dozen environment
    47  	// variables to be set correctly. Thus, we assume users to run cifuzz from
    48  	// a developer command prompt anyway and thus don't need to set the
    49  	// compiler explicitly.
    50  	if runtime.GOOS != "windows" {
    51  		// Set the C/C++ compiler to clang/clang++ (if not already set),
    52  		// which is needed to build a  binary with fuzzing instrumentation
    53  		// gcc doesn't have -fsanitize=fuzzer.
    54  		if val := envutil.GetEnvWithPathSubstring(env, "CC", "clang"); val == "" {
    55  			env, err = envutil.Setenv(env, "CC", "clang")
    56  			if err != nil {
    57  				return nil, err
    58  			}
    59  		}
    60  		if val := envutil.GetEnvWithPathSubstring(env, "CXX", "clang++"); val == "" {
    61  			env, err = envutil.Setenv(env, "CXX", "clang++")
    62  			if err != nil {
    63  				return nil, err
    64  			}
    65  		}
    66  	}
    67  
    68  	// We don't want to fail if ASan is set up incorrectly for tools
    69  	// built and executed during the build or they contain leaks.
    70  	env, err = envutil.Setenv(env, "ASAN_OPTIONS", "detect_leaks=0:verify_asan_link_order=0")
    71  	if err != nil {
    72  		return nil, err
    73  	}
    74  
    75  	return env, nil
    76  }
    77  
    78  var commonCFlags = []string{
    79  	// Keep debug symbols
    80  	"-g",
    81  	// Do optimizations which don't harm debugging
    82  	"-Og",
    83  	// To get good stack frames for better debugging
    84  	"-fno-omit-frame-pointer",
    85  	// Conventional macro to conditionally compile out fuzzer road blocks
    86  	// See https://llvm.org/docs/LibFuzzer.html#fuzzer-friendly-build-mode
    87  	"-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION",
    88  	// Ensure that asserts are enabled regardless of compilation mode (e.g. explicit -DNDEBUG).
    89  	"-UNDEBUG",
    90  }
    91  
    92  func LibFuzzerCFlags() []string {
    93  	// These flags must not contain spaces, because the environment
    94  	// variables that are set to these flags are space separated.
    95  	// Note: Keep in sync with share/cmake/cifuzz-functions.cmake
    96  	return append(commonCFlags, []string{
    97  		// ----- Flags used to build with libFuzzer -----
    98  		// Compile with edge coverage and compare instrumentation. We
    99  		// use fuzzer-no-link here instead of -fsanitize=fuzzer because
   100  		// CFLAGS are often also passed to the linker, which would cause
   101  		// errors if the build includes tools which have a main function.
   102  		"-fsanitize=fuzzer-no-link",
   103  
   104  		// ----- Flags used to build with ASan -----
   105  		// Build with instrumentation for ASan and UBSan and link in
   106  		// their runtime
   107  		"-fsanitize=address,undefined",
   108  		// To support recovering from ASan findings
   109  		"-fsanitize-recover=address",
   110  		// Use additional error detectors for use-after-scope bugs
   111  		// TODO: Evaluate the slow down caused by this flag
   112  		// TODO: Check if there are other additional error detectors
   113  		//       which we want to use
   114  		"-fsanitize-address-use-after-scope",
   115  		// Disable source fortification, which is currently not supported
   116  		// in combination with ASan, see https://github.com/google/sanitizers/issues/247
   117  		"-U_FORTIFY_SOURCE",
   118  	}...)
   119  }
   120  
   121  func CoverageCFlags(clangVersion *semver.Version) []string {
   122  	cflags := append(commonCFlags, []string{
   123  		// ----- Flags used to build with code coverage -----
   124  		"-fprofile-instr-generate",
   125  		"-fcoverage-mapping",
   126  		// Disable source fortification to ensure that coverage builds
   127  		// reach all code reached by ASan builds.
   128  		"-U_FORTIFY_SOURCE",
   129  	}...)
   130  
   131  	if runtime.GOOS != "darwin" && clangVersion != nil {
   132  		// LLVM's continuous mode requires compile-time support on non-macOS
   133  		// platforms. This support is unstable in Clang 13 and lower, so we
   134  		// only enable it on 14+.
   135  		if clangVersion.Compare(semver.MustParse("14.0.0")) >= 0 {
   136  			cflags = append(cflags, "-mllvm", "-runtime-counter-relocation")
   137  		}
   138  	}
   139  	return cflags
   140  }