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