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