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