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
tnfctl_exec_open(const char * pgm_name,char * const * args,char * const * envp,const char * ld_preload,const char * libtnfprobe_path,tnfctl_handle_t ** ret_val)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
tnfctl_pid_open(pid_t pid,tnfctl_handle_t ** ret_val)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
tnfctl_indirect_open(void * prochandle,tnfctl_ind_config_t * config,tnfctl_handle_t ** ret_val)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
tnfctl_kernel_open(tnfctl_handle_t ** ret_val)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
tnfctl_trace_attrs_get(tnfctl_handle_t * hdl,tnfctl_trace_attrs_t * attrs)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
tnfctl_buffer_alloc(tnfctl_handle_t * hdl,const char * trace_file_name,uint_t trace_file_size)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
tnfctl_buffer_dealloc(tnfctl_handle_t * hdl)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
attach_pid(pid_t pid,prb_proc_ctl_t ** proc_pp)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
step_to_end_of_exec(tnfctl_handle_t * hndl)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
_tnfctl_external_getlock(tnfctl_handle_t * hdl)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