1// Copyright 2022 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5//go:build linux || (freebsd && amd64) 6 7package sanitizers_test 8 9import ( 10 "internal/testenv" 11 "strings" 12 "testing" 13) 14 15func TestLibFuzzer(t *testing.T) { 16 // Skip tests in short mode. 17 if testing.Short() { 18 t.Skip("libfuzzer tests can take upwards of minutes to run; skipping in short mode") 19 } 20 testenv.MustHaveGoBuild(t) 21 testenv.MustHaveCGO(t) 22 23 goos, err := goEnv("GOOS") 24 if err != nil { 25 t.Fatal(err) 26 } 27 goarch, err := goEnv("GOARCH") 28 if err != nil { 29 t.Fatal(err) 30 } 31 if !libFuzzerSupported(goos, goarch) { 32 t.Skipf("skipping on %s/%s; libfuzzer option is not supported.", goos, goarch) 33 } 34 config := configure("fuzzer") 35 config.skipIfCSanitizerBroken(t) 36 37 cases := []struct { 38 goSrc string 39 cSrc string 40 expectedError string 41 }{ 42 {goSrc: "libfuzzer1.go", expectedError: "panic: found it"}, 43 {goSrc: "libfuzzer2.go", cSrc: "libfuzzer2.c", expectedError: "panic: found it"}, 44 } 45 for _, tc := range cases { 46 tc := tc 47 name := strings.TrimSuffix(tc.goSrc, ".go") 48 t.Run(name, func(t *testing.T) { 49 t.Parallel() 50 51 dir := newTempDir(t) 52 defer dir.RemoveAll(t) 53 54 // build Go code in libfuzzer mode to a c-archive 55 outPath := dir.Join(name) 56 archivePath := dir.Join(name + ".a") 57 mustRun(t, config.goCmd("build", "-buildmode=c-archive", "-o", archivePath, srcPath(tc.goSrc))) 58 59 // build C code (if any) and link with Go code 60 cmd, err := cc(config.cFlags...) 61 if err != nil { 62 t.Fatalf("error running cc: %v", err) 63 } 64 cmd.Args = append(cmd.Args, config.ldFlags...) 65 cmd.Args = append(cmd.Args, "-o", outPath, "-I", dir.Base()) 66 if tc.cSrc != "" { 67 cmd.Args = append(cmd.Args, srcPath(tc.cSrc)) 68 } 69 cmd.Args = append(cmd.Args, archivePath) 70 mustRun(t, cmd) 71 72 cmd = hangProneCmd(outPath) 73 cmd.Dir = dir.Base() 74 outb, err := cmd.CombinedOutput() 75 out := string(outb) 76 if err == nil { 77 t.Fatalf("fuzzing succeeded unexpectedly; output:\n%s", out) 78 } 79 if !strings.Contains(out, tc.expectedError) { 80 t.Errorf("exited without expected error %q; got\n%s", tc.expectedError, out) 81 } 82 }) 83 } 84} 85 86// libFuzzerSupported is a copy of the function internal/platform.FuzzInstrumented, 87// because the internal package can't be used here. 88func libFuzzerSupported(goos, goarch string) bool { 89 switch goarch { 90 case "amd64", "arm64": 91 // TODO(#14565): support more architectures. 92 switch goos { 93 case "darwin", "freebsd", "linux", "windows": 94 return true 95 default: 96 return false 97 } 98 default: 99 return false 100 } 101} 102