xref: /openbsd/sys/dev/dt/dt_prov_kprobe.c (revision 58a4fbf9)
1 /*	$OpenBSD: dt_prov_kprobe.c,v 1.9 2024/11/08 12:28:00 mpi Exp $	*/
2 
3 /*
4  * Copyright (c) 2020 Tom Rollet <tom.rollet@epita.fr>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 #if defined(DDBPROF) && (defined(__amd64__) || defined(__i386__))
19 
20 #include <sys/types.h>
21 #include <sys/systm.h>
22 #include <sys/param.h>
23 #include <sys/malloc.h>
24 #include <sys/atomic.h>
25 #include <sys/exec_elf.h>
26 
27 #include <ddb/db_elf.h>
28 #include <machine/db_machdep.h>
29 #include <ddb/db_extern.h>
30 #include <ddb/db_access.h>
31 #include <ddb/db_sym.h>
32 #include <ddb/db_interface.h>
33 
34 #include <dev/dt/dtvar.h>
35 
36 int dt_prov_kprobe_alloc(struct dt_probe *dtp, struct dt_softc *sc,
37     struct dt_pcb_list *plist, struct dtioc_req *dtrq);
38 int dt_prov_kprobe_hook(struct dt_provider *dtpv, ...);
39 int dt_prov_kprobe_dealloc(struct dt_probe *dtp, struct dt_softc *sc,
40     struct dtioc_req *dtrq);
41 
42 void	db_prof_count(struct trapframe *frame);
43 vaddr_t	db_get_probe_addr(struct trapframe *);
44 
45 struct kprobe_probe {
46 	struct dt_probe* dtp;
47 	SLIST_ENTRY(kprobe_probe) kprobe_next;
48 };
49 
50 /* Bob Jenkin's public domain 32-bit integer hashing function.
51  * Original at https://burtleburtle.net/bob/hash/integer.html.
52  */
53 uint32_t
ptr_hash(uint32_t a)54 ptr_hash(uint32_t a) {
55 	a = (a + 0x7ed55d16) + (a<<12);
56 	a = (a ^ 0xc761c23c) ^ (a>>19);
57 	a = (a + 0x165667b1) + (a<<5);
58 	a = (a + 0xd3a2646c) ^ (a<<9);
59 	a = (a + 0xfd7046c5) + (a<<3);
60 	a = (a ^ 0xb55a4f09) ^ (a>>16);
61 	return a;
62 }
63 
64 #define PPTSIZE		PAGE_SIZE * 30
65 #define	PPTMASK		((PPTSIZE / sizeof(struct kprobe_probe)) - 1)
66 #define INSTTOIDX(inst)	(ptr_hash(inst) & PPTMASK)
67 
68 SLIST_HEAD(, kprobe_probe) *dtpf_entry;
69 SLIST_HEAD(, kprobe_probe) *dtpf_return;
70 int nb_probes_entry =	0;
71 int nb_probes_return =	0;
72 
73 #define DTEVT_PROV_KPROBE (DTEVT_COMMON|DTEVT_FUNCARGS)
74 
75 #define KPROBE_ENTRY "entry"
76 #define KPROBE_RETURN "return"
77 
78 #if defined(__amd64__)
79 #define KPROBE_IBT_1	0xf3
80 #define KPROBE_IBT_2	0x0f
81 #define KPROBE_IBT_3	0x1e
82 #define KPROBE_IBT_4	0xfa
83 #define KPROBE_IBT_SIZE	4
84 
85 #define KPROBE_RETGUARD_MOV_1 0x4c
86 #define KPROBE_RETGUARD_MOV_2 0x8b
87 #define KPROBE_RETGUARD_MOV_3 0x1d
88 
89 #define KPROBE_RETGUARD_MOV_SIZE 7
90 
91 #define KPROBE_RETGUARD_XOR_1 0x4c
92 #define KPROBE_RETGUARD_XOR_2 0x33
93 #define KPROBE_RETGUARD_XOR_3 0x1c
94 
95 #define KPROBE_RETGUARD_XOR_SIZE 4
96 
97 #define RET_INST	0xc3
98 #define RET_SIZE	1
99 #elif defined(__i386__)
100 #define POP_RBP_INST	0x5d
101 #define POP_RBP_SIZE	1
102 #endif
103 
104 struct dt_provider dt_prov_kprobe = {
105 	.dtpv_name    = "kprobe",
106 	.dtpv_alloc   = dt_prov_kprobe_alloc,
107 	.dtpv_enter   = dt_prov_kprobe_hook,
108 	.dtpv_leave   = NULL,
109 	.dtpv_dealloc = dt_prov_kprobe_dealloc,
110 };
111 
112 extern db_symtab_t db_symtab;
113 extern char __kutext_end[];
114 extern int db_prof_on;
115 
116 /* Initialize all entry and return probes and store them in global arrays */
117 int
dt_prov_kprobe_init(void)118 dt_prov_kprobe_init(void)
119 {
120 	struct dt_probe *dtp;
121 	struct kprobe_probe *kprobe_dtp;
122 	Elf_Sym *symp, *symtab_start, *symtab_end;
123 	const char *strtab, *name;
124 	vaddr_t inst, limit;
125 	int nb_sym, nb_probes;
126 
127 	nb_sym = (db_symtab.end - db_symtab.start) / sizeof (Elf_Sym);
128 	nb_probes = nb_probes_entry = nb_probes_return = 0;
129 
130 	dtpf_entry = malloc(PPTSIZE, M_DT, M_NOWAIT|M_ZERO);
131 	if (dtpf_entry == NULL)
132 		goto end;
133 
134 	dtpf_return = malloc(PPTSIZE, M_DT, M_NOWAIT|M_ZERO);
135 	if (dtpf_return == NULL)
136 		goto end;
137 
138 	db_symtab_t *stab = &db_symtab;
139 
140 	symtab_start = STAB_TO_SYMSTART(stab);
141 	symtab_end = STAB_TO_SYMEND(stab);
142 
143 	strtab = db_elf_find_strtab(stab);
144 
145 	for (symp = symtab_start; symp < symtab_end; symp++) {
146 		if (ELF_ST_TYPE(symp->st_info) != STT_FUNC)
147 			continue;
148 
149 		inst = symp->st_value;
150 		name = strtab + symp->st_name;
151 		limit = symp->st_value + symp->st_size;
152 
153 		/* Filter function that are not mapped in memory */
154 		if (inst < KERNBASE || inst >= (vaddr_t)&__kutext_end)
155 			continue;
156 
157 		/* Remove some function to avoid recursive tracing */
158 		if (strncmp(name, "dt_", 3) == 0 || strncmp(name, "trap", 4) == 0 ||
159 		    strncmp(name, "db_", 3) == 0)
160 			continue;
161 
162 #if defined(__amd64__)
163 		/*
164 		 * Find the IBT target and the retguard which follows it.
165 		 * Move the instruction pointer down to the 'push rbp' as needed.
166 		 */
167 		if (*((uint8_t *)inst) != SSF_INST) {
168 			if (((uint8_t *)inst)[0] != KPROBE_IBT_1 ||
169 				((uint8_t *)inst)[1] != KPROBE_IBT_2 ||
170 				((uint8_t *)inst)[2] != KPROBE_IBT_3 ||
171 				((uint8_t *)inst)[3] != KPROBE_IBT_4)
172 				continue;
173 
174 			if (((uint8_t *)inst)[KPROBE_IBT_SIZE] != KPROBE_RETGUARD_MOV_1 ||
175 				((uint8_t *)inst)[KPROBE_IBT_SIZE + 1] != KPROBE_RETGUARD_MOV_2 ||
176 				((uint8_t *)inst)[KPROBE_IBT_SIZE + 2] != KPROBE_RETGUARD_MOV_3 ||
177 				((uint8_t *)inst)[KPROBE_IBT_SIZE + KPROBE_RETGUARD_MOV_SIZE] != KPROBE_RETGUARD_XOR_1 ||
178 				((uint8_t *)inst)[KPROBE_IBT_SIZE + KPROBE_RETGUARD_MOV_SIZE + 1] != KPROBE_RETGUARD_XOR_2 ||
179 				((uint8_t *)inst)[KPROBE_IBT_SIZE + KPROBE_RETGUARD_MOV_SIZE + 2] != KPROBE_RETGUARD_XOR_3 ||
180 				((uint8_t *)inst)[KPROBE_IBT_SIZE + KPROBE_RETGUARD_MOV_SIZE + KPROBE_RETGUARD_XOR_SIZE] != SSF_INST)
181 				continue;
182 			inst = (vaddr_t)&(((uint8_t *)inst)[KPROBE_IBT_SIZE + KPROBE_RETGUARD_MOV_SIZE + KPROBE_RETGUARD_XOR_SIZE]);
183 		}
184 #elif defined(__i386__)
185 		/* No retguard or IBT on i386 */
186 		if (*((uint8_t *)inst) != SSF_INST)
187 			continue;
188 #endif
189 
190 		dtp = dt_dev_alloc_probe(name, KPROBE_ENTRY, &dt_prov_kprobe);
191 		if (dtp == NULL)
192 			goto end;
193 
194 		kprobe_dtp = malloc(sizeof(struct kprobe_probe), M_TEMP, M_NOWAIT|M_ZERO);
195 		if (kprobe_dtp == NULL)
196 			goto end;
197 		kprobe_dtp->dtp = dtp;
198 
199 		dtp->dtp_addr = inst;
200 		dtp->dtp_nargs = db_ctf_func_numargs(symp);
201 		dt_dev_register_probe(dtp);
202 
203 		SLIST_INSERT_HEAD(&dtpf_entry[INSTTOIDX(dtp->dtp_addr)], kprobe_dtp, kprobe_next);
204 
205 		nb_probes++;
206 		nb_probes_entry++;
207 
208 #if defined(__amd64__)
209 		/* If there last instruction isn't a ret, just bail. */
210 		if (*(uint8_t *)(limit - 1) != RET_INST)
211 			continue;
212 		inst = limit - 1;
213 #elif defined(__i386__)
214 		/*
215 		 * Little temporary hack to find some return probe
216 		 *   => always int3 after 'pop %rpb; ret'
217 		 */
218 		while(*((uint8_t *)inst) == 0xcc)
219 			(*(uint8_t *)inst) -= 1;
220 		if (*(uint8_t *)(limit - 2) != POP_RBP)
221 			continue;
222 		inst = limit - 2;
223 #endif
224 
225 		dtp = dt_dev_alloc_probe(name, KPROBE_RETURN, &dt_prov_kprobe);
226 		if (dtp == NULL)
227 			goto end;
228 
229 		kprobe_dtp = malloc(sizeof(struct kprobe_probe), M_TEMP, M_NOWAIT|M_ZERO);
230 		if (kprobe_dtp == NULL)
231 			goto end;
232 		kprobe_dtp->dtp = dtp;
233 
234 		dtp->dtp_addr = inst;
235 		dt_dev_register_probe(dtp);
236 		SLIST_INSERT_HEAD(&dtpf_return[INSTTOIDX(dtp->dtp_addr)], kprobe_dtp, kprobe_next);
237 		nb_probes++;
238 		nb_probes_return++;
239 	}
240 end:
241 	return nb_probes;
242 }
243 
244 int
dt_prov_kprobe_alloc(struct dt_probe * dtp,struct dt_softc * sc,struct dt_pcb_list * plist,struct dtioc_req * dtrq)245 dt_prov_kprobe_alloc(struct dt_probe *dtp, struct dt_softc *sc,
246     struct dt_pcb_list *plist, struct dtioc_req *dtrq)
247 {
248 	uint8_t patch = BKPT_INST;
249 	struct dt_pcb *dp;
250 	unsigned s;
251 
252 	dp = dt_pcb_alloc(dtp, sc);
253 	if (dp == NULL)
254 		return ENOMEM;
255 
256 	/* Patch only if it's first pcb referencing this probe */
257 	dtp->dtp_ref++;
258 	KASSERT(dtp->dtp_ref != 0);
259 
260 	if (dtp->dtp_ref == 1) {
261 		s = intr_disable();
262 		db_write_bytes(dtp->dtp_addr, BKPT_SIZE, &patch);
263 		intr_restore(s);
264 	}
265 
266 	dp->dp_evtflags = dtrq->dtrq_evtflags & DTEVT_PROV_KPROBE;
267 	TAILQ_INSERT_HEAD(plist, dp, dp_snext);
268 	return 0;
269 }
270 
271 int
dt_prov_kprobe_dealloc(struct dt_probe * dtp,struct dt_softc * sc,struct dtioc_req * dtrq)272 dt_prov_kprobe_dealloc(struct dt_probe *dtp, struct dt_softc *sc,
273    struct dtioc_req *dtrq)
274 {
275 	uint8_t patch;
276 	int size;
277 	unsigned s;
278 
279 	if (strcmp(dtp->dtp_name, KPROBE_ENTRY) == 0) {
280 		patch = SSF_INST;
281 		size  = SSF_SIZE;
282 	} else if (strcmp(dtp->dtp_name, KPROBE_RETURN) == 0) {
283 #if defined(__amd64__)
284 		patch = RET_INST;
285 		size  = RET_SIZE;
286 #elif defined(__i386__)
287 		patch = POP_RBP_INST;
288 		size  = POP_RBP_SIZE;
289 #endif
290 	} else
291 		panic("Trying to dealloc not yet implemented probe type");
292 
293 	dtp->dtp_ref--;
294 
295 	if (dtp->dtp_ref == 0) {
296 		s = intr_disable();
297 		db_write_bytes(dtp->dtp_addr, size, &patch);
298 		intr_restore(s);
299 	}
300 
301 	/* Deallocation of PCB is done by dt_pcb_purge when closing the dev */
302 	return 0;
303 }
304 
305 int
dt_prov_kprobe_hook(struct dt_provider * dtpv,...)306 dt_prov_kprobe_hook(struct dt_provider *dtpv, ...)
307 {
308 	struct dt_probe *dtp;
309 	struct dt_pcb *dp;
310 	struct trapframe *tf;
311 	struct kprobe_probe *kprobe_dtp;
312 	va_list ap;
313 	int is_dt_bkpt = 0;
314 	int error;	/* Return values for return probes*/
315 	vaddr_t *args, addr;
316 	size_t argsize;
317 	register_t retval[2];
318 
319 	KASSERT(dtpv == &dt_prov_kprobe);
320 
321 	va_start(ap, dtpv);
322 	tf = va_arg(ap, struct trapframe*);
323 	va_end(ap);
324 
325 	addr = db_get_probe_addr(tf);
326 
327 	SLIST_FOREACH(kprobe_dtp, &dtpf_entry[INSTTOIDX(addr)], kprobe_next) {
328 		dtp = kprobe_dtp->dtp;
329 
330 		if (dtp->dtp_addr != addr)
331 			continue;
332 
333 		is_dt_bkpt = 1;
334 		if (db_prof_on)
335 			db_prof_count(tf);
336 
337 		if (!dtp->dtp_recording)
338 			continue;
339 
340 		smr_read_enter();
341 		SMR_SLIST_FOREACH(dp, &dtp->dtp_pcbs, dp_pnext) {
342 			struct dt_evt *dtev;
343 
344 			dtev = dt_pcb_ring_get(dp, 0);
345 			if (dtev == NULL)
346 				continue;
347 
348 #if defined(__amd64__)
349 			args = (vaddr_t *)tf->tf_rdi;
350 			/* XXX: use CTF to get the number of arguments. */
351 			argsize = 6;
352 #elif defined(__i386__)
353 			/* All args on stack */
354 			args = (vaddr_t *)(tf->tf_esp + 4);
355 			argsize = 10;
356 #endif
357 
358 			if (ISSET(dp->dp_evtflags, DTEVT_FUNCARGS))
359 				memcpy(dtev->dtev_args, args, argsize);
360 
361 			dt_pcb_ring_consume(dp, dtev);
362 		}
363 		smr_read_leave();
364 	}
365 
366 	if (is_dt_bkpt)
367 		return is_dt_bkpt;
368 
369 	SLIST_FOREACH(kprobe_dtp, &dtpf_return[INSTTOIDX(addr)], kprobe_next) {
370 		dtp = kprobe_dtp->dtp;
371 
372 		if (dtp->dtp_addr != addr)
373 			continue;
374 
375 		is_dt_bkpt = 2;
376 
377 		if (!dtp->dtp_recording)
378 			continue;
379 
380 		smr_read_enter();
381 		SMR_SLIST_FOREACH(dp, &dtp->dtp_pcbs, dp_pnext) {
382 			struct dt_evt *dtev;
383 
384 			dtev = dt_pcb_ring_get(dp, 0);
385 			if (dtev == NULL)
386 				continue;
387 
388 #if defined(__amd64__)
389 			retval[0] = tf->tf_rax;
390 			retval[1] = 0;
391 			error = 0;
392 #elif defined(__i386)
393 			retval[0] = tf->tf_eax;
394 			retval[1] = 0;
395 			error = 0;
396 #endif
397 
398 			dtev->dtev_retval[0] = retval[0];
399 			dtev->dtev_retval[1] = retval[1];
400 			dtev->dtev_error = error;
401 
402 			dt_pcb_ring_consume(dp, dtev);
403 		}
404 		smr_read_leave();
405 	}
406 	return is_dt_bkpt;
407 }
408 
409 /* Function called by ddb to patch all functions without allocating 1 pcb per probe */
410 void
dt_prov_kprobe_patch_all_entry(void)411 dt_prov_kprobe_patch_all_entry(void)
412 {
413 	uint8_t patch = BKPT_INST;
414 	struct dt_probe *dtp;
415 	struct kprobe_probe *kprobe_dtp;
416 	size_t i;
417 
418 	for (i = 0; i < PPTMASK; ++i) {
419 		SLIST_FOREACH(kprobe_dtp, &dtpf_entry[i], kprobe_next) {
420 			dtp = kprobe_dtp->dtp;
421 			dtp->dtp_ref++;
422 
423 			if (dtp->dtp_ref == 1) {
424 				unsigned s;
425 				s = intr_disable();
426 				db_write_bytes(dtp->dtp_addr, BKPT_SIZE, &patch);
427 				intr_restore(s);
428 			}
429 		}
430 	}
431 }
432 
433 /* Function called by ddb to patch all functions without allocating 1 pcb per probe */
434 void
dt_prov_kprobe_depatch_all_entry(void)435 dt_prov_kprobe_depatch_all_entry(void)
436 {
437 	uint8_t patch = SSF_INST;
438 	struct dt_probe *dtp;
439 	struct kprobe_probe *kprobe_dtp;
440 	size_t i;
441 
442 	for (i = 0; i < PPTMASK; ++i) {
443 		SLIST_FOREACH(kprobe_dtp, &dtpf_entry[i], kprobe_next) {
444 			dtp = kprobe_dtp->dtp;
445 			dtp->dtp_ref--;
446 
447 			if (dtp->dtp_ref == 0) {
448 				unsigned s;
449 				s = intr_disable();
450 				db_write_bytes(dtp->dtp_addr, SSF_SIZE, &patch);
451 				intr_restore(s);
452 			}
453 		}
454 
455 	}
456 }
457 #endif /* __amd64__ || __i386__ */
458