xref: /titanic_44/usr/src/lib/libtnfctl/open.c (revision 49b225e1cfa7bbf7738d4df0a03f18e3283426eb)
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