1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright (c) 1994, by Sun Microsytems, Inc. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 /* 29 * Interfaces that return a tnfctl handle back to client (except for 30 * tnfctl_internal_open()) and helper functions for these interfaces. 31 * Also has buffer alloc, buffer dealloc, and trace attributes retrieval 32 * interfaces. 33 */ 34 35 #include "tnfctl_int.h" 36 #include "kernel_int.h" 37 #include "dbg.h" 38 39 #include <stdlib.h> 40 #include <unistd.h> 41 #include <signal.h> 42 #include <errno.h> 43 44 static tnfctl_errcode_t attach_pid(pid_t pid, prb_proc_ctl_t **proc_pp); 45 static tnfctl_errcode_t step_to_end_of_exec(tnfctl_handle_t *hndl); 46 47 /* 48 * invokes the target program and executes it till the run time linker (rtld) 49 * has loaded in the shared objects (but before any .init sections are 50 * executed). Returns a pointer to a tnfctl handle. 51 */ 52 tnfctl_errcode_t 53 tnfctl_exec_open(const char *pgm_name, char * const *args, char * const *envp, 54 const char *ld_preload, 55 const char *libtnfprobe_path, 56 tnfctl_handle_t **ret_val) 57 { 58 tnfctl_handle_t *hdl; 59 prb_proc_ctl_t *proc_p = NULL; 60 prb_status_t prbstat; 61 uintptr_t dbgaddr; 62 tnfctl_errcode_t prexstat; 63 64 prbstat = prb_child_create(pgm_name, args, ld_preload, libtnfprobe_path, 65 envp, &proc_p); 66 if (prbstat) { 67 return (_tnfctl_map_to_errcode(prbstat)); 68 } 69 70 /* allocate hdl and zero fill */ 71 hdl = calloc(1, sizeof (*hdl)); 72 if (hdl == NULL) { 73 (void) prb_proc_close(proc_p); 74 return (TNFCTL_ERR_ALLOCFAIL); 75 } 76 77 hdl->proc_p = proc_p; 78 hdl->mode = DIRECT_MODE; 79 hdl->called_exit = B_FALSE; 80 81 /* use native /proc on this target */ 82 hdl->p_read = _tnfctl_read_targ; 83 hdl->p_write = _tnfctl_write_targ; 84 hdl->p_obj_iter = _tnfctl_loadobj_iter; 85 hdl->p_getpid = _tnfctl_pid_get; 86 87 /* 88 * get the address of DT_DEBUG and send it in to prb_ layer. 89 * This is needed before before prb_rtld_sync() can be called. 90 */ 91 prexstat = _tnfctl_elf_dbgent(hdl, &dbgaddr); 92 if (prexstat) 93 goto failure_ret; 94 95 prb_dbgaddr(proc_p, dbgaddr); 96 97 /* sync up to rtld sync point */ 98 prbstat = prb_rtld_sync_if_needed(proc_p); 99 if (prbstat) { 100 prexstat = _tnfctl_map_to_errcode(prbstat); 101 goto failure_ret; 102 } 103 104 /* initialize state in handle */ 105 prexstat = _tnfctl_set_state(hdl); 106 if (prexstat) 107 goto failure_ret; 108 109 prexstat = _tnfctl_external_getlock(hdl); 110 if (prexstat) 111 goto failure_ret; 112 113 *ret_val = hdl; 114 /* Successful return */ 115 return (TNFCTL_ERR_NONE); 116 117 failure_ret: 118 (void) prb_proc_close(proc_p); 119 free(hdl); 120 return (prexstat); 121 } 122 123 124 /* 125 * attaches to a running process. If the process is in the beginning 126 * of an exec(2) system call (which is how tnfctl_continue() returns on exec), 127 * it steps the process till the end of the the exec. If the process hasn't 128 * reached the rtld sync point, the process is continued until it does 129 * reach it. Returns a pointer to a tnfctl handle. 130 */ 131 tnfctl_errcode_t 132 tnfctl_pid_open(pid_t pid, tnfctl_handle_t **ret_val) 133 { 134 tnfctl_handle_t *hdl; 135 prb_proc_ctl_t *proc_p = NULL; 136 uintptr_t dbgaddr; 137 prb_status_t prbstat; 138 tnfctl_errcode_t prexstat; 139 140 prexstat = attach_pid(pid, &proc_p); 141 if (prexstat) { 142 return (prexstat); 143 } 144 145 /* allocate hdl and zero fill */ 146 hdl = calloc(1, sizeof (*hdl)); 147 if (hdl == NULL) { 148 (void) prb_proc_close(proc_p); 149 return (TNFCTL_ERR_ALLOCFAIL); 150 } 151 152 hdl->proc_p = proc_p; 153 hdl->mode = DIRECT_MODE; 154 hdl->called_exit = B_FALSE; 155 156 /* use native /proc on this target */ 157 hdl->p_read = _tnfctl_read_targ; 158 hdl->p_write = _tnfctl_write_targ; 159 hdl->p_obj_iter = _tnfctl_loadobj_iter; 160 hdl->p_getpid = _tnfctl_pid_get; 161 162 /* 163 * Since tnfctl_continue() returns when a process does an exec 164 * and leaves the process stopped at the beginning of exec, we 165 * have to be sure to catch this case. 166 */ 167 prexstat = step_to_end_of_exec(hdl); 168 /* proc_p could be side effected by step_to_end_of_exec() */ 169 proc_p = hdl->proc_p; 170 if (prexstat) 171 goto failure_ret; 172 173 /* 174 * get the address of DT_DEBUG and send it in to prb_ layer. 175 */ 176 prexstat = _tnfctl_elf_dbgent(hdl, &dbgaddr); 177 if (prexstat) 178 goto failure_ret; 179 180 prb_dbgaddr(proc_p, dbgaddr); 181 182 /* sync up to rtld sync point if target is not there yet */ 183 prbstat = prb_rtld_sync_if_needed(proc_p); 184 if (prbstat) { 185 prexstat = _tnfctl_map_to_errcode(prbstat); 186 goto failure_ret; 187 } 188 189 /* initialize state in handle */ 190 prexstat = _tnfctl_set_state(hdl); 191 if (prexstat) 192 goto failure_ret; 193 194 /* set state in target indicating we're tracing externally */ 195 prexstat = _tnfctl_external_getlock(hdl); 196 if (prexstat) 197 goto failure_ret; 198 199 *ret_val = hdl; 200 201 /* Sucessful return */ 202 return (TNFCTL_ERR_NONE); 203 204 failure_ret: 205 (void) prb_proc_close(proc_p); 206 free(hdl); 207 return (prexstat); 208 } 209 210 /* 211 * open a process for tracing without using native /proc on it. The client 212 * provides a set of callback functions which encapsulate the /proc 213 * functionality we need. Returns a pointer to a tnfctl handle. 214 */ 215 tnfctl_errcode_t 216 tnfctl_indirect_open(void *prochandle, tnfctl_ind_config_t *config, 217 tnfctl_handle_t **ret_val) 218 { 219 tnfctl_handle_t *hdl; 220 tnfctl_errcode_t prexstat; 221 222 /* allocate hdl and zero fill */ 223 hdl = calloc(1, sizeof (*hdl)); 224 if (hdl == NULL) { 225 return (TNFCTL_ERR_ALLOCFAIL); 226 } 227 228 hdl->proc_p = prochandle; 229 hdl->mode = INDIRECT_MODE; 230 hdl->called_exit = B_FALSE; 231 232 /* initialize callback functions */ 233 hdl->p_read = config->p_read; 234 hdl->p_write = config->p_write; 235 hdl->p_obj_iter = config->p_obj_iter; 236 hdl->p_getpid = config->p_getpid; 237 238 /* initialize state in handle */ 239 prexstat = _tnfctl_set_state(hdl); 240 if (prexstat) { 241 free(hdl); 242 return (prexstat); 243 } 244 /* set state in target indicating we're tracing externally */ 245 prexstat = _tnfctl_external_getlock(hdl); 246 if (prexstat) { 247 free(hdl); 248 return (prexstat); 249 } 250 *ret_val = hdl; 251 return (TNFCTL_ERR_NONE); 252 } 253 254 /* 255 * Returns a pointer to a tnfctl handle that can do kernel trace control 256 * and kernel probe control. 257 */ 258 tnfctl_errcode_t 259 tnfctl_kernel_open(tnfctl_handle_t **ret_val) 260 { 261 tnfctl_handle_t *hdl; 262 tnfctl_errcode_t prexstat; 263 264 /* allocate hdl and zero fill */ 265 hdl = calloc(1, sizeof (*hdl)); 266 if (hdl == NULL) { 267 return (TNFCTL_ERR_ALLOCFAIL); 268 } 269 270 /* initialize kernel tracing */ 271 prexstat = _tnfctl_prbk_init(hdl); 272 if (prexstat) 273 return (prexstat); 274 275 hdl->mode = KERNEL_MODE; 276 hdl->targ_pid = 0; 277 278 /* initialize function pointers that can be stuffed into a probe */ 279 _tnfctl_prbk_get_other_funcs(&hdl->allocfunc, &hdl->commitfunc, 280 &hdl->rollbackfunc, &hdl->endfunc); 281 _tnfctl_prbk_test_func(&hdl->testfunc); 282 283 /* find the probes in the kernel */ 284 prexstat = _tnfctl_refresh_kernel(hdl); 285 if (prexstat) 286 return (prexstat); 287 288 *ret_val = hdl; 289 return (TNFCTL_ERR_NONE); 290 } 291 292 /* 293 * Returns the trace attributes to the client. Since there can be 294 * only one controlling agent on a target at a time, our cached information 295 * is correct and we don't have to actually retrieve any information 296 * from the target. 297 */ 298 tnfctl_errcode_t 299 tnfctl_trace_attrs_get(tnfctl_handle_t *hdl, tnfctl_trace_attrs_t *attrs) 300 { 301 boolean_t release_lock; 302 tnfctl_errcode_t prexstat; 303 304 /*LINTED statement has no consequent: else*/ 305 LOCK_SYNC(hdl, prexstat, release_lock); 306 307 attrs->targ_pid = hdl->targ_pid; 308 attrs->trace_file_name = hdl->trace_file_name; 309 attrs->trace_buf_size = hdl->trace_buf_size; 310 attrs->trace_min_size = hdl->trace_min_size; 311 attrs->trace_buf_state = hdl->trace_buf_state; 312 attrs->trace_state = hdl->trace_state; 313 attrs->filter_state = hdl->kpidfilter_state; 314 315 /*LINTED statement has no consequent: else*/ 316 UNLOCK(hdl, release_lock); 317 318 return (TNFCTL_ERR_NONE); 319 } 320 321 322 /* 323 * Allocate a trace buffer of the specified name and size. 324 */ 325 tnfctl_errcode_t 326 tnfctl_buffer_alloc(tnfctl_handle_t *hdl, const char *trace_file_name, 327 uint_t trace_file_size) 328 { 329 tnfctl_errcode_t prexstat; 330 331 if (hdl->mode == KERNEL_MODE) { 332 /* trace_file_name is ignored in kernel mode */ 333 prexstat = _tnfctl_prbk_buffer_alloc(hdl, trace_file_size); 334 if (prexstat) 335 return (prexstat); 336 return (TNFCTL_ERR_NONE); 337 } 338 339 /* Not KERNEL_MODE */ 340 if (hdl->trace_file_name != NULL) { 341 /* buffer already allocated */ 342 return (TNFCTL_ERR_BUFEXISTS); 343 } 344 345 prexstat = _tnfctl_create_tracefile(hdl, trace_file_name, 346 trace_file_size); 347 if (prexstat) { 348 return (prexstat); 349 } 350 351 return (TNFCTL_ERR_NONE); 352 } 353 354 /* 355 * Deallocate the trace buffer - only works for kernel mode 356 */ 357 tnfctl_errcode_t 358 tnfctl_buffer_dealloc(tnfctl_handle_t *hdl) 359 { 360 tnfctl_errcode_t prexstat; 361 362 if (hdl->mode != KERNEL_MODE) 363 return (TNFCTL_ERR_BADARG); 364 365 /* KERNEL_MODE */ 366 prexstat = _tnfctl_prbk_buffer_dealloc(hdl); 367 if (prexstat) 368 return (prexstat); 369 return (TNFCTL_ERR_NONE); 370 } 371 372 373 /* 374 * Helper function for attaching to a target process 375 */ 376 static tnfctl_errcode_t 377 attach_pid(pid_t pid, prb_proc_ctl_t **proc_pp) 378 { 379 prb_status_t prbstat; 380 prb_proc_ctl_t *proc_p; 381 382 if (getpid() == pid) 383 return (TNFCTL_ERR_BADARG); 384 385 /* check if pid is valid */ 386 if ((kill(pid, 0) == -1) && errno == ESRCH) { 387 return (TNFCTL_ERR_NOPROCESS); 388 } 389 /* open up /proc fd */ 390 prbstat = prb_proc_open(pid, proc_pp); 391 if (prbstat) 392 return (_tnfctl_map_to_errcode(prbstat)); 393 394 proc_p = *proc_pp; 395 /* 396 * default is to run-on-last-close. In case we cannot sync with 397 * target, we don't want to kill the target. 398 */ 399 prbstat = prb_proc_setrlc(proc_p, B_TRUE); 400 if (prbstat) 401 goto failure_ret; 402 prbstat = prb_proc_setklc(proc_p, B_FALSE); 403 if (prbstat) 404 goto failure_ret; 405 406 /* stop process */ 407 prbstat = prb_proc_stop(proc_p); 408 if (prbstat) 409 goto failure_ret; 410 411 /* Sucessful return */ 412 return (TNFCTL_ERR_NONE); 413 414 failure_ret: 415 (void) prb_proc_close(proc_p); 416 return (_tnfctl_map_to_errcode(prbstat)); 417 } 418 419 /* 420 * Checks if target is at the beginning of an exec system call. If so, 421 * it runs it till the end of the exec system call. It takes care of 422 * the case where you're about to exec a setuid program. 423 * CAUTION: could side effect hndl->proc_p 424 */ 425 static tnfctl_errcode_t 426 step_to_end_of_exec(tnfctl_handle_t *hndl) 427 { 428 prb_proc_ctl_t *proc_p, *oldproc_p; 429 prb_status_t prbstat, tempstat; 430 int pid; 431 prb_proc_state_t pstate; 432 433 proc_p = hndl->proc_p; 434 pid = hndl->p_getpid(proc_p); 435 436 prbstat = prb_proc_state(proc_p, &pstate); 437 if (prbstat) 438 return (_tnfctl_map_to_errcode(prbstat)); 439 if (!(pstate.ps_issysentry && (pstate.ps_syscallnum == SYS_exec || 440 pstate.ps_syscallnum == SYS_execve))) { 441 /* not stopped at beginning of exec system call */ 442 return (TNFCTL_ERR_NONE); 443 } 444 445 /* we are stopped at beginning of exec system call */ 446 447 /* REMIND: do we have to wait on SYS_exec also ? */ 448 prbstat = prb_proc_exit(proc_p, SYS_execve, PRB_SYS_ADD); 449 if (prbstat) 450 return (_tnfctl_map_to_errcode(prbstat)); 451 452 prbstat = prb_proc_cont(proc_p); 453 if (prbstat) 454 return (_tnfctl_map_to_errcode(prbstat)); 455 456 prbstat = prb_proc_wait(proc_p, B_FALSE, NULL); 457 switch (prbstat) { 458 case PRB_STATUS_OK: 459 break; 460 case EAGAIN: 461 /* 462 * If we had exec'ed a setuid/setgid program PIOCWSTOP 463 * will return EAGAIN. Reopen the 'fd' and try again. 464 * Read the last section of /proc man page - we reopen first 465 * and then close the old fd. 466 */ 467 oldproc_p = proc_p; 468 tempstat = prb_proc_reopen(pid, &proc_p); 469 if (tempstat) { 470 /* here EACCES means exec'ed a setuid/setgid program */ 471 return (_tnfctl_map_to_errcode(tempstat)); 472 } 473 474 prb_proc_close(oldproc_p); 475 hndl->proc_p = proc_p; 476 break; 477 default: 478 return (_tnfctl_map_to_errcode(prbstat)); 479 } 480 481 prbstat = prb_proc_state(proc_p, &pstate); 482 if (prbstat) 483 return (_tnfctl_map_to_errcode(prbstat)); 484 485 if (!(pstate.ps_issysexit && (pstate.ps_syscallnum == SYS_execve))) { 486 /* unexpected condition */ 487 return (tnfctl_status_map(ENOENT)); 488 } 489 490 /* clear old interest mask */ 491 prbstat = prb_proc_exit(proc_p, SYS_execve, PRB_SYS_DEL); 492 if (prbstat) 493 return (_tnfctl_map_to_errcode(prbstat)); 494 return (TNFCTL_ERR_NONE); 495 } 496 497 498 tnfctl_errcode_t 499 _tnfctl_external_getlock(tnfctl_handle_t *hdl) 500 { 501 502 tnfctl_errcode_t prexstat; 503 prb_status_t prbstat; 504 uintptr_t targ_symbol_ptr; 505 int internal_tracing_on; 506 507 prexstat = _tnfctl_sym_find(hdl, TNFCTL_INTERNAL_TRACEFLAG, 508 &targ_symbol_ptr); 509 if (prexstat) { 510 /* no libtnfctl in target: success */ 511 return (TNFCTL_ERR_NONE); 512 } 513 prbstat = hdl->p_read(hdl->proc_p, targ_symbol_ptr, 514 &internal_tracing_on, sizeof (internal_tracing_on)); 515 516 if (prbstat) { 517 prexstat = _tnfctl_map_to_errcode(prbstat); 518 goto failure_ret; 519 } 520 if (internal_tracing_on) { 521 /* target process being traced internally */ 522 prexstat = TNFCTL_ERR_BUSY; 523 goto failure_ret; 524 } 525 prexstat = _tnfctl_sym_find(hdl, TNFCTL_EXTERNAL_TRACEDPID, 526 &targ_symbol_ptr); 527 if (prexstat) { 528 /* this shouldn't happen. we know we have libtnfctl */ 529 goto failure_ret; 530 } 531 prbstat = hdl->p_write(hdl->proc_p, targ_symbol_ptr, 532 &(hdl->targ_pid), sizeof (hdl->targ_pid)); 533 if (prbstat) { 534 prexstat = _tnfctl_map_to_errcode(prbstat); 535 goto failure_ret; 536 } 537 /* success */ 538 DBG((void) fprintf(stderr, "_tnfctl_external_getlock: ok to trace %d\n", 539 hdl->targ_pid)); 540 return (TNFCTL_ERR_NONE); 541 542 failure_ret: 543 return (prexstat); 544 } 545