1// Copyright 2019 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 libfuzzer
6
7package runtime
8
9import "unsafe"
10
11func libfuzzerCallWithTwoByteBuffers(fn, start, end *byte)
12func libfuzzerCallTraceIntCmp(fn *byte, arg0, arg1, fakePC uintptr)
13func libfuzzerCall4(fn *byte, fakePC uintptr, s1, s2 unsafe.Pointer, result uintptr)
14
15// Keep in sync with the definition of ret_sled in src/runtime/libfuzzer_amd64.s
16const retSledSize = 512
17
18// In libFuzzer mode, the compiler inserts calls to libfuzzerTraceCmpN and libfuzzerTraceConstCmpN
19// (where N can be 1, 2, 4, or 8) for encountered integer comparisons in the code to be instrumented.
20// This may result in these functions having callers that are nosplit. That is why they must be nosplit.
21//
22//go:nosplit
23func libfuzzerTraceCmp1(arg0, arg1 uint8, fakePC uint) {
24	fakePC = fakePC % retSledSize
25	libfuzzerCallTraceIntCmp(&__sanitizer_cov_trace_cmp1, uintptr(arg0), uintptr(arg1), uintptr(fakePC))
26}
27
28//go:nosplit
29func libfuzzerTraceCmp2(arg0, arg1 uint16, fakePC uint) {
30	fakePC = fakePC % retSledSize
31	libfuzzerCallTraceIntCmp(&__sanitizer_cov_trace_cmp2, uintptr(arg0), uintptr(arg1), uintptr(fakePC))
32}
33
34//go:nosplit
35func libfuzzerTraceCmp4(arg0, arg1 uint32, fakePC uint) {
36	fakePC = fakePC % retSledSize
37	libfuzzerCallTraceIntCmp(&__sanitizer_cov_trace_cmp4, uintptr(arg0), uintptr(arg1), uintptr(fakePC))
38}
39
40//go:nosplit
41func libfuzzerTraceCmp8(arg0, arg1 uint64, fakePC uint) {
42	fakePC = fakePC % retSledSize
43	libfuzzerCallTraceIntCmp(&__sanitizer_cov_trace_cmp8, uintptr(arg0), uintptr(arg1), uintptr(fakePC))
44}
45
46//go:nosplit
47func libfuzzerTraceConstCmp1(arg0, arg1 uint8, fakePC uint) {
48	fakePC = fakePC % retSledSize
49	libfuzzerCallTraceIntCmp(&__sanitizer_cov_trace_const_cmp1, uintptr(arg0), uintptr(arg1), uintptr(fakePC))
50}
51
52//go:nosplit
53func libfuzzerTraceConstCmp2(arg0, arg1 uint16, fakePC uint) {
54	fakePC = fakePC % retSledSize
55	libfuzzerCallTraceIntCmp(&__sanitizer_cov_trace_const_cmp2, uintptr(arg0), uintptr(arg1), uintptr(fakePC))
56}
57
58//go:nosplit
59func libfuzzerTraceConstCmp4(arg0, arg1 uint32, fakePC uint) {
60	fakePC = fakePC % retSledSize
61	libfuzzerCallTraceIntCmp(&__sanitizer_cov_trace_const_cmp4, uintptr(arg0), uintptr(arg1), uintptr(fakePC))
62}
63
64//go:nosplit
65func libfuzzerTraceConstCmp8(arg0, arg1 uint64, fakePC uint) {
66	fakePC = fakePC % retSledSize
67	libfuzzerCallTraceIntCmp(&__sanitizer_cov_trace_const_cmp8, uintptr(arg0), uintptr(arg1), uintptr(fakePC))
68}
69
70var pcTables []byte
71
72func init() {
73	libfuzzerCallWithTwoByteBuffers(&__sanitizer_cov_8bit_counters_init, &__start___sancov_cntrs, &__stop___sancov_cntrs)
74	start := unsafe.Pointer(&__start___sancov_cntrs)
75	end := unsafe.Pointer(&__stop___sancov_cntrs)
76
77	// PC tables are arrays of ptr-sized integers representing pairs [PC,PCFlags] for every instrumented block.
78	// The number of PCs and PCFlags is the same as the number of 8-bit counters. Each PC table entry has
79	// the size of two ptr-sized integers. We allocate one more byte than what we actually need so that we can
80	// get a pointer representing the end of the PC table array.
81	size := (uintptr(end)-uintptr(start))*unsafe.Sizeof(uintptr(0))*2 + 1
82	pcTables = make([]byte, size)
83	libfuzzerCallWithTwoByteBuffers(&__sanitizer_cov_pcs_init, &pcTables[0], &pcTables[size-1])
84}
85
86// We call libFuzzer's __sanitizer_weak_hook_strcmp function which takes the
87// following four arguments:
88//
89//  1. caller_pc: location of string comparison call site
90//  2. s1: first string used in the comparison
91//  3. s2: second string used in the comparison
92//  4. result: an integer representing the comparison result. 0 indicates
93//     equality (comparison will ignored by libfuzzer), non-zero indicates a
94//     difference (comparison will be taken into consideration).
95//
96//go:nosplit
97func libfuzzerHookStrCmp(s1, s2 string, fakePC int) {
98	if s1 != s2 {
99		libfuzzerCall4(&__sanitizer_weak_hook_strcmp, uintptr(fakePC), cstring(s1), cstring(s2), uintptr(1))
100	}
101	// if s1 == s2 we could call the hook with a last argument of 0 but this is unnecessary since this case will be then
102	// ignored by libfuzzer
103}
104
105// This function has now the same implementation as libfuzzerHookStrCmp because we lack better checks
106// for case-insensitive string equality in the runtime package.
107//
108//go:nosplit
109func libfuzzerHookEqualFold(s1, s2 string, fakePC int) {
110	if s1 != s2 {
111		libfuzzerCall4(&__sanitizer_weak_hook_strcmp, uintptr(fakePC), cstring(s1), cstring(s2), uintptr(1))
112	}
113}
114
115//go:linkname __sanitizer_cov_trace_cmp1 __sanitizer_cov_trace_cmp1
116//go:cgo_import_static __sanitizer_cov_trace_cmp1
117var __sanitizer_cov_trace_cmp1 byte
118
119//go:linkname __sanitizer_cov_trace_cmp2 __sanitizer_cov_trace_cmp2
120//go:cgo_import_static __sanitizer_cov_trace_cmp2
121var __sanitizer_cov_trace_cmp2 byte
122
123//go:linkname __sanitizer_cov_trace_cmp4 __sanitizer_cov_trace_cmp4
124//go:cgo_import_static __sanitizer_cov_trace_cmp4
125var __sanitizer_cov_trace_cmp4 byte
126
127//go:linkname __sanitizer_cov_trace_cmp8 __sanitizer_cov_trace_cmp8
128//go:cgo_import_static __sanitizer_cov_trace_cmp8
129var __sanitizer_cov_trace_cmp8 byte
130
131//go:linkname __sanitizer_cov_trace_const_cmp1 __sanitizer_cov_trace_const_cmp1
132//go:cgo_import_static __sanitizer_cov_trace_const_cmp1
133var __sanitizer_cov_trace_const_cmp1 byte
134
135//go:linkname __sanitizer_cov_trace_const_cmp2 __sanitizer_cov_trace_const_cmp2
136//go:cgo_import_static __sanitizer_cov_trace_const_cmp2
137var __sanitizer_cov_trace_const_cmp2 byte
138
139//go:linkname __sanitizer_cov_trace_const_cmp4 __sanitizer_cov_trace_const_cmp4
140//go:cgo_import_static __sanitizer_cov_trace_const_cmp4
141var __sanitizer_cov_trace_const_cmp4 byte
142
143//go:linkname __sanitizer_cov_trace_const_cmp8 __sanitizer_cov_trace_const_cmp8
144//go:cgo_import_static __sanitizer_cov_trace_const_cmp8
145var __sanitizer_cov_trace_const_cmp8 byte
146
147//go:linkname __sanitizer_cov_8bit_counters_init __sanitizer_cov_8bit_counters_init
148//go:cgo_import_static __sanitizer_cov_8bit_counters_init
149var __sanitizer_cov_8bit_counters_init byte
150
151// start, stop markers of counters, set by the linker
152var __start___sancov_cntrs, __stop___sancov_cntrs byte
153
154//go:linkname __sanitizer_cov_pcs_init __sanitizer_cov_pcs_init
155//go:cgo_import_static __sanitizer_cov_pcs_init
156var __sanitizer_cov_pcs_init byte
157
158//go:linkname __sanitizer_weak_hook_strcmp __sanitizer_weak_hook_strcmp
159//go:cgo_import_static __sanitizer_weak_hook_strcmp
160var __sanitizer_weak_hook_strcmp byte
161