xref: /freebsd/sys/kern/subr_coverage.c (revision 5bc6a91f)
1524553f5SAndrew Turner /*-
2524553f5SAndrew Turner  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3524553f5SAndrew Turner  *
4524553f5SAndrew Turner  * Copyright (C) 2018 The FreeBSD Foundation. All rights reserved.
5524553f5SAndrew Turner  * Copyright (C) 2018, 2019 Andrew Turner
6524553f5SAndrew Turner  *
7524553f5SAndrew Turner  * This software was developed by Mitchell Horne under sponsorship of
8524553f5SAndrew Turner  * the FreeBSD Foundation.
9524553f5SAndrew Turner  *
10524553f5SAndrew Turner  * This software was developed by SRI International and the University of
11524553f5SAndrew Turner  * Cambridge Computer Laboratory under DARPA/AFRL contract FA8750-10-C-0237
12524553f5SAndrew Turner  * ("CTSRD"), as part of the DARPA CRASH research programme.
13524553f5SAndrew Turner  *
14524553f5SAndrew Turner  * Redistribution and use in source and binary forms, with or without
15524553f5SAndrew Turner  * modification, are permitted provided that the following conditions
16524553f5SAndrew Turner  * are met:
17524553f5SAndrew Turner  * 1. Redistributions of source code must retain the above copyright
18524553f5SAndrew Turner  *    notice, this list of conditions and the following disclaimer.
19524553f5SAndrew Turner  * 2. Redistributions in binary form must reproduce the above copyright
20524553f5SAndrew Turner  *    notice, this list of conditions and the following disclaimer in the
21524553f5SAndrew Turner  *    documentation and/or other materials provided with the distribution.
22524553f5SAndrew Turner  *
23524553f5SAndrew Turner  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
24524553f5SAndrew Turner  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25524553f5SAndrew Turner  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26524553f5SAndrew Turner  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
27524553f5SAndrew Turner  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28524553f5SAndrew Turner  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29524553f5SAndrew Turner  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30524553f5SAndrew Turner  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31524553f5SAndrew Turner  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32524553f5SAndrew Turner  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33524553f5SAndrew Turner  * SUCH DAMAGE.
34524553f5SAndrew Turner  *
35524553f5SAndrew Turner  * $FreeBSD$
36524553f5SAndrew Turner  */
37524553f5SAndrew Turner 
38524553f5SAndrew Turner #include <sys/cdefs.h>
39524553f5SAndrew Turner __FBSDID("$FreeBSD$");
40524553f5SAndrew Turner 
41524553f5SAndrew Turner #include <sys/param.h>
42524553f5SAndrew Turner #include <sys/coverage.h>
43524553f5SAndrew Turner 
44524553f5SAndrew Turner #include <machine/atomic.h>
45524553f5SAndrew Turner 
46524553f5SAndrew Turner void __sanitizer_cov_trace_pc(void);
47524553f5SAndrew Turner void __sanitizer_cov_trace_cmp1(uint8_t, uint8_t);
48524553f5SAndrew Turner void __sanitizer_cov_trace_cmp2(uint16_t, uint16_t);
49524553f5SAndrew Turner void __sanitizer_cov_trace_cmp4(uint32_t, uint32_t);
50524553f5SAndrew Turner void __sanitizer_cov_trace_cmp8(uint64_t, uint64_t);
51524553f5SAndrew Turner void __sanitizer_cov_trace_const_cmp1(uint8_t, uint8_t);
52524553f5SAndrew Turner void __sanitizer_cov_trace_const_cmp2(uint16_t, uint16_t);
53524553f5SAndrew Turner void __sanitizer_cov_trace_const_cmp4(uint32_t, uint32_t);
54524553f5SAndrew Turner void __sanitizer_cov_trace_const_cmp8(uint64_t, uint64_t);
55524553f5SAndrew Turner void __sanitizer_cov_trace_switch(uint64_t, uint64_t *);
56524553f5SAndrew Turner 
57524553f5SAndrew Turner static cov_trace_pc_t cov_trace_pc;
58524553f5SAndrew Turner static cov_trace_cmp_t cov_trace_cmp;
59524553f5SAndrew Turner 
60524553f5SAndrew Turner void
61524553f5SAndrew Turner cov_register_pc(cov_trace_pc_t trace_pc)
62524553f5SAndrew Turner {
63524553f5SAndrew Turner 
64524553f5SAndrew Turner 	atomic_store_ptr(&cov_trace_pc, trace_pc);
65524553f5SAndrew Turner }
66524553f5SAndrew Turner 
67524553f5SAndrew Turner void
68524553f5SAndrew Turner cov_unregister_pc(void)
69524553f5SAndrew Turner {
70524553f5SAndrew Turner 
71524553f5SAndrew Turner 	atomic_store_ptr(&cov_trace_pc, NULL);
72524553f5SAndrew Turner }
73524553f5SAndrew Turner 
74524553f5SAndrew Turner void
75524553f5SAndrew Turner cov_register_cmp(cov_trace_cmp_t trace_cmp)
76524553f5SAndrew Turner {
77524553f5SAndrew Turner 
78524553f5SAndrew Turner 	atomic_store_ptr(&cov_trace_cmp, trace_cmp);
79524553f5SAndrew Turner }
80524553f5SAndrew Turner 
81524553f5SAndrew Turner void
82524553f5SAndrew Turner cov_unregister_cmp(void)
83524553f5SAndrew Turner {
84524553f5SAndrew Turner 
85524553f5SAndrew Turner 	atomic_store_ptr(&cov_trace_cmp, NULL);
86524553f5SAndrew Turner }
87524553f5SAndrew Turner 
88524553f5SAndrew Turner /*
89524553f5SAndrew Turner  * Main entry point. A call to this function will be inserted
90524553f5SAndrew Turner  * at every edge, and if coverage is enabled for the thread
91524553f5SAndrew Turner  * this function will add the PC to the buffer.
92524553f5SAndrew Turner  */
93524553f5SAndrew Turner void
94524553f5SAndrew Turner __sanitizer_cov_trace_pc(void)
95524553f5SAndrew Turner {
96524553f5SAndrew Turner 	cov_trace_pc_t trace_pc;
97524553f5SAndrew Turner 
985bc6a91fSMateusz Guzik 	trace_pc = atomic_load_ptr(&cov_trace_pc);
99524553f5SAndrew Turner 	if (trace_pc != NULL)
100524553f5SAndrew Turner 		trace_pc((uint64_t)__builtin_return_address(0));
101524553f5SAndrew Turner }
102524553f5SAndrew Turner 
103524553f5SAndrew Turner /*
104524553f5SAndrew Turner  * Comparison entry points. When the kernel performs a comparison
105524553f5SAndrew Turner  * operation the compiler inserts a call to one of the following
106524553f5SAndrew Turner  * functions to record the operation.
107524553f5SAndrew Turner  */
108524553f5SAndrew Turner void
109524553f5SAndrew Turner __sanitizer_cov_trace_cmp1(uint8_t arg1, uint8_t arg2)
110524553f5SAndrew Turner {
111524553f5SAndrew Turner 	cov_trace_cmp_t trace_cmp;
112524553f5SAndrew Turner 
1135bc6a91fSMateusz Guzik 	trace_cmp = atomic_load_ptr(&cov_trace_cmp);
114524553f5SAndrew Turner 	if (trace_cmp != NULL)
115524553f5SAndrew Turner 		trace_cmp(COV_CMP_SIZE(0), arg1, arg2,
116524553f5SAndrew Turner 		    (uint64_t)__builtin_return_address(0));
117524553f5SAndrew Turner }
118524553f5SAndrew Turner 
119524553f5SAndrew Turner void
120524553f5SAndrew Turner __sanitizer_cov_trace_cmp2(uint16_t arg1, uint16_t arg2)
121524553f5SAndrew Turner {
122524553f5SAndrew Turner 	cov_trace_cmp_t trace_cmp;
123524553f5SAndrew Turner 
1245bc6a91fSMateusz Guzik 	trace_cmp = atomic_load_ptr(&cov_trace_cmp);
125524553f5SAndrew Turner 	if (trace_cmp != NULL)
126524553f5SAndrew Turner 		trace_cmp(COV_CMP_SIZE(1), arg1, arg2,
127524553f5SAndrew Turner 		    (uint64_t)__builtin_return_address(0));
128524553f5SAndrew Turner }
129524553f5SAndrew Turner 
130524553f5SAndrew Turner void
131524553f5SAndrew Turner __sanitizer_cov_trace_cmp4(uint32_t arg1, uint32_t arg2)
132524553f5SAndrew Turner {
133524553f5SAndrew Turner 	cov_trace_cmp_t trace_cmp;
134524553f5SAndrew Turner 
1355bc6a91fSMateusz Guzik 	trace_cmp = atomic_load_ptr(&cov_trace_cmp);
136524553f5SAndrew Turner 	if (trace_cmp != NULL)
137524553f5SAndrew Turner 		trace_cmp(COV_CMP_SIZE(2), arg1, arg2,
138524553f5SAndrew Turner 		    (uint64_t)__builtin_return_address(0));
139524553f5SAndrew Turner }
140524553f5SAndrew Turner 
141524553f5SAndrew Turner void
142524553f5SAndrew Turner __sanitizer_cov_trace_cmp8(uint64_t arg1, uint64_t arg2)
143524553f5SAndrew Turner {
144524553f5SAndrew Turner 	cov_trace_cmp_t trace_cmp;
145524553f5SAndrew Turner 
1465bc6a91fSMateusz Guzik 	trace_cmp = atomic_load_ptr(&cov_trace_cmp);
147524553f5SAndrew Turner 	if (trace_cmp != NULL)
148524553f5SAndrew Turner 		trace_cmp(COV_CMP_SIZE(3), arg1, arg2,
149524553f5SAndrew Turner 		    (uint64_t)__builtin_return_address(0));
150524553f5SAndrew Turner }
151524553f5SAndrew Turner 
152524553f5SAndrew Turner void
153524553f5SAndrew Turner __sanitizer_cov_trace_const_cmp1(uint8_t arg1, uint8_t arg2)
154524553f5SAndrew Turner {
155524553f5SAndrew Turner 	cov_trace_cmp_t trace_cmp;
156524553f5SAndrew Turner 
1575bc6a91fSMateusz Guzik 	trace_cmp = atomic_load_ptr(&cov_trace_cmp);
158524553f5SAndrew Turner 	if (trace_cmp != NULL)
159524553f5SAndrew Turner 		trace_cmp(COV_CMP_SIZE(0) | COV_CMP_CONST, arg1, arg2,
160524553f5SAndrew Turner 		    (uint64_t)__builtin_return_address(0));
161524553f5SAndrew Turner }
162524553f5SAndrew Turner 
163524553f5SAndrew Turner void
164524553f5SAndrew Turner __sanitizer_cov_trace_const_cmp2(uint16_t arg1, uint16_t arg2)
165524553f5SAndrew Turner {
166524553f5SAndrew Turner 	cov_trace_cmp_t trace_cmp;
167524553f5SAndrew Turner 
1685bc6a91fSMateusz Guzik 	trace_cmp = atomic_load_ptr(&cov_trace_cmp);
169524553f5SAndrew Turner 	if (trace_cmp != NULL)
170524553f5SAndrew Turner 		trace_cmp(COV_CMP_SIZE(1) | COV_CMP_CONST, arg1, arg2,
171524553f5SAndrew Turner 		    (uint64_t)__builtin_return_address(0));
172524553f5SAndrew Turner }
173524553f5SAndrew Turner 
174524553f5SAndrew Turner void
175524553f5SAndrew Turner __sanitizer_cov_trace_const_cmp4(uint32_t arg1, uint32_t arg2)
176524553f5SAndrew Turner {
177524553f5SAndrew Turner 	cov_trace_cmp_t trace_cmp;
178524553f5SAndrew Turner 
1795bc6a91fSMateusz Guzik 	trace_cmp = atomic_load_ptr(&cov_trace_cmp);
180524553f5SAndrew Turner 	if (trace_cmp != NULL)
181524553f5SAndrew Turner 		trace_cmp(COV_CMP_SIZE(2) | COV_CMP_CONST, arg1, arg2,
182524553f5SAndrew Turner 		    (uint64_t)__builtin_return_address(0));
183524553f5SAndrew Turner }
184524553f5SAndrew Turner 
185524553f5SAndrew Turner void
186524553f5SAndrew Turner __sanitizer_cov_trace_const_cmp8(uint64_t arg1, uint64_t arg2)
187524553f5SAndrew Turner {
188524553f5SAndrew Turner 	cov_trace_cmp_t trace_cmp;
189524553f5SAndrew Turner 
1905bc6a91fSMateusz Guzik 	trace_cmp = atomic_load_ptr(&cov_trace_cmp);
191524553f5SAndrew Turner 	if (trace_cmp != NULL)
192524553f5SAndrew Turner 		trace_cmp(COV_CMP_SIZE(3) | COV_CMP_CONST, arg1, arg2,
193524553f5SAndrew Turner 		    (uint64_t)__builtin_return_address(0));
194524553f5SAndrew Turner }
195524553f5SAndrew Turner 
196524553f5SAndrew Turner /*
197524553f5SAndrew Turner  * val is the switch operand
198524553f5SAndrew Turner  * cases[0] is the number of case constants
199524553f5SAndrew Turner  * cases[1] is the size of val in bits
200524553f5SAndrew Turner  * cases[2..n] are the case constants
201524553f5SAndrew Turner  */
202524553f5SAndrew Turner void
203524553f5SAndrew Turner __sanitizer_cov_trace_switch(uint64_t val, uint64_t *cases)
204524553f5SAndrew Turner {
205524553f5SAndrew Turner 	uint64_t i, count, ret, type;
206524553f5SAndrew Turner 	cov_trace_cmp_t trace_cmp;
207524553f5SAndrew Turner 
2085bc6a91fSMateusz Guzik 	trace_cmp = atomic_load_ptr(&cov_trace_cmp);
209524553f5SAndrew Turner 	if (trace_cmp == NULL)
210524553f5SAndrew Turner 		return;
211524553f5SAndrew Turner 
212524553f5SAndrew Turner 	count = cases[0];
213524553f5SAndrew Turner 	ret = (uint64_t)__builtin_return_address(0);
214524553f5SAndrew Turner 
215524553f5SAndrew Turner 	switch (cases[1]) {
216524553f5SAndrew Turner 	case 8:
217524553f5SAndrew Turner 		type = COV_CMP_SIZE(0);
218524553f5SAndrew Turner 		break;
219524553f5SAndrew Turner 	case 16:
220524553f5SAndrew Turner 		type = COV_CMP_SIZE(1);
221524553f5SAndrew Turner 		break;
222524553f5SAndrew Turner 	case 32:
223524553f5SAndrew Turner 		type = COV_CMP_SIZE(2);
224524553f5SAndrew Turner 		break;
225524553f5SAndrew Turner 	case 64:
226524553f5SAndrew Turner 		type = COV_CMP_SIZE(3);
227524553f5SAndrew Turner 		break;
228524553f5SAndrew Turner 	default:
229524553f5SAndrew Turner 		return;
230524553f5SAndrew Turner 	}
231524553f5SAndrew Turner 
232524553f5SAndrew Turner 	val |= COV_CMP_CONST;
233524553f5SAndrew Turner 
234524553f5SAndrew Turner 	for (i = 0; i < count; i++)
235524553f5SAndrew Turner 		if (!trace_cmp(type, val, cases[i + 2], ret))
236524553f5SAndrew Turner 			return;
237524553f5SAndrew Turner }
238