From 2b0e457b42a64455ca2d3eebb5c6d4e6acfc5db2 Mon Sep 17 00:00:00 2001 From: Khaled Yakdan Date: Fri, 20 May 2022 22:09:58 +0000 Subject: cmd/compile: intercept string compares in libFuzzer mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit IR string compares as well as calls to string comparison functions such as `strings.EqualFold` are intercepted and the corresponding libFuzzer callbacks are invoked with the corresponding arguments. As a result, the compared strings will be added to libFuzzer’s table of recent compares, which feeds future mutations performed by the fuzzer and thus allow it to reach into branches guarded by string comparisons. The list of methods to intercept is maintained in `cmd/compile/internal/walk/expr.go` and can easily be extended to cover more standard library functions in the future. Change-Id: I5c8b89499c4e19459406795dea923bf777779c51 GitHub-Last-Rev: 6b8529b55561faf57ea59cb7cff1caf8c9c94ecd GitHub-Pull-Request: golang/go#51319 Reviewed-on: https://go-review.googlesource.com/c/go/+/387335 Reviewed-by: Keith Randall Reviewed-by: Michael Knyszek TryBot-Result: Gopher Robot Reviewed-by: Keith Randall Run-TryBot: Keith Randall --- src/runtime/libfuzzer.go | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) (limited to 'src/runtime/libfuzzer.go') diff --git a/src/runtime/libfuzzer.go b/src/runtime/libfuzzer.go index 920ac575f5..c136eaf5fe 100644 --- a/src/runtime/libfuzzer.go +++ b/src/runtime/libfuzzer.go @@ -9,6 +9,7 @@ package runtime import "unsafe" func libfuzzerCallWithTwoByteBuffers(fn, start, end *byte) +func libfuzzerCall4(fn *byte, fakePC uintptr, s1, s2 unsafe.Pointer, result uintptr) func libfuzzerCall(fn *byte, arg0, arg1 uintptr) func libfuzzerTraceCmp1(arg0, arg1 uint8) { @@ -59,6 +60,31 @@ func init() { libfuzzerCallWithTwoByteBuffers(&__sanitizer_cov_pcs_init, &pcTables[0], &pcTables[size-1]) } +// We call libFuzzer's __sanitizer_weak_hook_strcmp function +// which takes the following four arguments: +// 1- caller_pc: location of string comparison call site +// 2- s1: first string used in the comparison +// 3- s2: second string used in the comparison +// 4- result: an integer representing the comparison result. Libfuzzer only distinguishes between two cases: +// - 0 means that the strings are equal and the comparison will be ignored by libfuzzer. +// - Any other value means that strings are not equal and libfuzzer takes the comparison into consideration. +// Here, we pass 1 when the strings are not equal. +func libfuzzerHookStrCmp(s1, s2 string, fakePC int) { + if s1 != s2 { + libfuzzerCall4(&__sanitizer_weak_hook_strcmp, uintptr(fakePC), cstring(s1), cstring(s2), uintptr(1)) + } + // if s1 == s2 we could call the hook with a last argument of 0 but this is unnecessary since this case will be then + // ignored by libfuzzer +} + +// This function has now the same implementation as libfuzzerHookStrCmp because we lack better checks +// for case-insensitive string equality in the runtime package. +func libfuzzerHookEqualFold(s1, s2 string, fakePC int) { + if s1 != s2 { + libfuzzerCall4(&__sanitizer_weak_hook_strcmp, uintptr(fakePC), cstring(s1), cstring(s2), uintptr(1)) + } +} + //go:linkname __sanitizer_cov_trace_cmp1 __sanitizer_cov_trace_cmp1 //go:cgo_import_static __sanitizer_cov_trace_cmp1 var __sanitizer_cov_trace_cmp1 byte @@ -106,3 +132,7 @@ var __stop___sancov_cntrs byte //go:linkname __sanitizer_cov_pcs_init __sanitizer_cov_pcs_init //go:cgo_import_static __sanitizer_cov_pcs_init var __sanitizer_cov_pcs_init byte + +//go:linkname __sanitizer_weak_hook_strcmp __sanitizer_weak_hook_strcmp +//go:cgo_import_static __sanitizer_weak_hook_strcmp +var __sanitizer_weak_hook_strcmp byte -- cgit v1.3