Skip to content

Commit e104af2

Browse files
committed
Currently, there isn't a reliable way to build
Go test binaries with libfuzzer instrumentation [1]. An attempt such as `bazel run --@io_bazel_rules_go//go/config:gc_goopts=-d=libfuzzer ...` fails to compile owing to unresolved symbols, e.g., runtime.libfuzzerTraceConstCmp8 [2]. This patch adds `libfuzzer_shim.go` (identical to `trace.go` [2]) to the `bzltestutil` package, which ensures the shim is linked with a Go test binary. Furthermore, we exclude `-d=libfuzzer` when compiling any of the _external_ dependencies. [1] bazel-contrib#3088 (comment) [2] golang/go@74f49f3
1 parent 0e7e4e3 commit e104af2

File tree

3 files changed

+60
-0
lines changed

3 files changed

+60
-0
lines changed

go/tools/builders/compilepkg.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727
"os/exec"
2828
"path"
2929
"path/filepath"
30+
"slices"
3031
"sort"
3132
"strings"
3233
)
@@ -343,6 +344,17 @@ func compileArchive(
343344
cgoSrcs[i-len(goSrcs)] = coverSrc
344345
}
345346
}
347+
if strings.Contains(outLinkObj, "external/") && slices.Contains(gcFlags, "-d=libfuzzer") {
348+
// Remove -d=libfuzzer from gcFlags when compiling external packages. We don't really want to instrument them,
349+
// and they may not link without libfuzzer_shim.go.
350+
gcFlags = slices.DeleteFunc(gcFlags, func(s string) bool {
351+
return s == "-d=libfuzzer"
352+
})
353+
}
354+
// Log instrumented objs for ease of tracking/debugging.
355+
if slices.Contains(gcFlags, "-d=libfuzzer") {
356+
fmt.Printf("%s -- gcFlags=%s\n", outLinkObj, gcFlags)
357+
}
346358

347359
// If we have cgo, generate separate C and go files, and compile the
348360
// C files.

go/tools/builders/stdlib.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"path/filepath"
2323
"regexp"
2424
"strings"
25+
"slices"
2526
)
2627

2728
// stdlib builds the standard library in the appropriate mode into a new goroot.
@@ -154,6 +155,12 @@ You may need to use the flags --cpu=x64_windows --compiler=mingw-gcc.`)
154155
break
155156
}
156157
}
158+
// Remove -d=libfuzzer from gcflags when compiling stdlib. We only want our code to be instrumented.
159+
// N.B. allowing this flag to pass may cause issues when linking external tools, e.g., protoc-bin, owing
160+
// to unresolved symbols, i.e., libfuzzer_shim.go isn't included.
161+
gcflags = slices.DeleteFunc(gcflags, func(s string) bool {
162+
return s == "-d=libfuzzer"
163+
})
157164
installArgs = append(installArgs, "-gcflags="+allSlug+strings.Join(gcflags, " "))
158165
installArgs = append(installArgs, "-ldflags="+allSlug+strings.Join(ldflags, " "))
159166
installArgs = append(installArgs, "-asmflags="+allSlug+strings.Join(asmflags, " "))
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Copyright 2021 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+
// N.B. This source is lifted verbatim from trace.go, added in
6+
// https://github.com/golang/go/commit/74f49f3366826f95a464cc15838a0668c92e3357
7+
//
8+
// It's essentially a shim to allow linking a Go test binary without libfuzzer.
9+
// We chose the 'bzltestutil' package because it's a bazel dependency for all Go test binaries.
10+
11+
//go:build !libfuzzer
12+
13+
package bzltestutil
14+
15+
import _ "unsafe" // for go:linkname
16+
17+
//go:linkname libfuzzerTraceCmp1 runtime.libfuzzerTraceCmp1
18+
//go:linkname libfuzzerTraceCmp2 runtime.libfuzzerTraceCmp2
19+
//go:linkname libfuzzerTraceCmp4 runtime.libfuzzerTraceCmp4
20+
//go:linkname libfuzzerTraceCmp8 runtime.libfuzzerTraceCmp8
21+
22+
//go:linkname libfuzzerTraceConstCmp1 runtime.libfuzzerTraceConstCmp1
23+
//go:linkname libfuzzerTraceConstCmp2 runtime.libfuzzerTraceConstCmp2
24+
//go:linkname libfuzzerTraceConstCmp4 runtime.libfuzzerTraceConstCmp4
25+
//go:linkname libfuzzerTraceConstCmp8 runtime.libfuzzerTraceConstCmp8
26+
27+
//go:linkname libfuzzerHookStrCmp runtime.libfuzzerHookStrCmp
28+
//go:linkname libfuzzerHookEqualFold runtime.libfuzzerHookEqualFold
29+
30+
func libfuzzerTraceCmp1(arg0, arg1 uint8, fakePC int) {}
31+
func libfuzzerTraceCmp2(arg0, arg1 uint16, fakePC int) {}
32+
func libfuzzerTraceCmp4(arg0, arg1 uint32, fakePC int) {}
33+
func libfuzzerTraceCmp8(arg0, arg1 uint64, fakePC int) {}
34+
35+
func libfuzzerTraceConstCmp1(arg0, arg1 uint8, fakePC int) {}
36+
func libfuzzerTraceConstCmp2(arg0, arg1 uint16, fakePC int) {}
37+
func libfuzzerTraceConstCmp4(arg0, arg1 uint32, fakePC int) {}
38+
func libfuzzerTraceConstCmp8(arg0, arg1 uint64, fakePC int) {}
39+
40+
func libfuzzerHookStrCmp(arg0, arg1 string, fakePC int) {}
41+
func libfuzzerHookEqualFold(arg0, arg1 string, fakePC int) {}

0 commit comments

Comments
 (0)