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 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 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 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 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 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 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 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