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 /*
27  * Interfaces to control kernel tracing and kernel probes
28  */
29 
30 #ifndef DEBUG
31 #define	NDEBUG	1
32 #endif
33 
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <unistd.h>
37 #include <string.h>	/* for strerror() */
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 #include <sys/tnf.h>
41 #include <fcntl.h>
42 #include <errno.h>
43 #include <signal.h>
44 #include <assert.h>
45 
46 #include "tnfctl_int.h"
47 #include "kernel_int.h"
48 
49 /* The TNF pseudo-device */
50 #define	TNFDRIVER	"/dev/tnfctl"
51 
52 /* Dummy "test" function  -- just used to flag enabled probes */
53 #define	PRBK_DUMMY_TEST	((tnf_probe_test_func_t) 4)
54 
55 /* Dummy "commit" function -- just used to flag trace enabled */
56 #define	PRBK_DUMMY_COMMIT ((tnf_probe_func_t) 8)
57 
58 /* Dummy "rollback" function -- just used to flag trace disabled */
59 #define	PRBK_DUMMY_ROLLBACK ((tnf_probe_func_t) 12)
60 
61 /* Dummy "end" function */
62 #define	PRBK_DUMMY_END ((uintptr_t) 16)
63 
64 /* Dummy "alloc" function */
65 #define	PRBK_DUMMY_ALLOC ((uintptr_t) 20)
66 
67 /* Minimum and maximum allowed buffer sizes. */
68 /* XXX -- maximum should be some function of physmem. */
69 #define	KERNEL_MINBUF_SIZE	(128 * 1024)
70 #define	KERNEL_MAXBUF_SIZE	(128 * 1024 * 1024)
71 
72 static tnfctl_errcode_t prbk_get_buf_attrs(tnfctl_handle_t *hdl);
73 static tnfctl_errcode_t alloc_probe_space(tnfctl_handle_t *hndl, int maxprobe);
74 
75 /*
76  * Initialize the kernel interface:  Open the TNF control device,
77  * and determine the current kernel probes state, including the
78  * current pidfilter list.
79  */
80 tnfctl_errcode_t
_tnfctl_prbk_init(tnfctl_handle_t * hdl)81 _tnfctl_prbk_init(tnfctl_handle_t *hdl)
82 {
83 	tnfctl_errcode_t prexstat;
84 	tifiocstate_t kstate;
85 	int kfd;
86 
87 	kfd = open(TNFDRIVER, O_RDWR);
88 	if (kfd < 0) {
89 		return (tnfctl_status_map(errno));
90 	}
91 	if (ioctl(kfd, TIFIOCGSTATE, &kstate) < 0)
92 		return (tnfctl_status_map(errno));
93 
94 	hdl->kfd = kfd;
95 	hdl->kpidfilter_state = kstate.pidfilter_mode;
96 	hdl->trace_state = !kstate.trace_stopped;
97 	hdl->trace_min_size = KERNEL_MINBUF_SIZE;
98 	prexstat = prbk_get_buf_attrs(hdl);
99 	if (prexstat)
100 		return (prexstat);
101 
102 	return (TNFCTL_ERR_NONE);
103 }
104 
105 /*
106  * Close the TNF control device.
107  */
108 tnfctl_errcode_t
_tnfctl_prbk_close(tnfctl_handle_t * hdl)109 _tnfctl_prbk_close(tnfctl_handle_t *hdl)
110 {
111 	if (hdl == NULL)
112 		return (TNFCTL_ERR_NONE);
113 
114 	if (close(hdl->kfd) == -1) {
115 		return (tnfctl_status_map(errno));
116 	}
117 	return (TNFCTL_ERR_NONE);
118 }
119 
120 /*
121  * Returns function addresses that can be plugged into function pointers
122  * in kernel probes.  These are actually dummy values that get
123  * interpreted by a routine in this file when a probe is flushed.
124  */
125 void
_tnfctl_prbk_get_other_funcs(uintptr_t * allocp,uintptr_t * commitp,uintptr_t * rollbackp,uintptr_t * endp)126 _tnfctl_prbk_get_other_funcs(uintptr_t *allocp, uintptr_t *commitp,
127 	uintptr_t *rollbackp, uintptr_t *endp)
128 {
129 	*allocp = PRBK_DUMMY_ALLOC;
130 	*commitp = (uintptr_t) PRBK_DUMMY_COMMIT;
131 	*rollbackp = (uintptr_t) PRBK_DUMMY_ROLLBACK;
132 	*endp = PRBK_DUMMY_END;
133 }
134 
135 
136 /*
137  * Returns test function address
138  */
139 void
_tnfctl_prbk_test_func(uintptr_t * outp)140 _tnfctl_prbk_test_func(uintptr_t *outp)
141 {
142 	*outp = (uintptr_t) PRBK_DUMMY_TEST;
143 }
144 
145 /*
146  * Allocate a trace buffer.  Check for reasonable size; reject if there's
147  * already a buffer.
148  */
149 tnfctl_errcode_t
_tnfctl_prbk_buffer_alloc(tnfctl_handle_t * hdl,int size)150 _tnfctl_prbk_buffer_alloc(tnfctl_handle_t *hdl, int size)
151 {
152 	tifiocstate_t bufstat;
153 	tnfctl_errcode_t prexstat;
154 	int saved_val;
155 
156 	if (ioctl(hdl->kfd, TIFIOCGSTATE, &bufstat) < 0) {
157 		return (tnfctl_status_map(errno));
158 	}
159 	if (bufstat.buffer_state != TIFIOCBUF_NONE) {
160 		return (TNFCTL_ERR_BUFEXISTS);
161 	}
162 	if (size < KERNEL_MINBUF_SIZE) {
163 		return (TNFCTL_ERR_SIZETOOSMALL);
164 	} else if (size > KERNEL_MAXBUF_SIZE) {
165 		/* REMIND: make this an error ? */
166 		size = KERNEL_MAXBUF_SIZE;
167 	}
168 	if (ioctl(hdl->kfd, TIFIOCALLOCBUF, size) < 0) {
169 		saved_val = errno;
170 		(void) prbk_get_buf_attrs(hdl);
171 		return (tnfctl_status_map(saved_val));
172 	}
173 
174 	prexstat = prbk_get_buf_attrs(hdl);
175 	if (prexstat)
176 		return (prexstat);
177 
178 	return (TNFCTL_ERR_NONE);
179 }
180 
181 /*
182  * Deallocate the kernel's trace buffer.
183  */
184 tnfctl_errcode_t
_tnfctl_prbk_buffer_dealloc(tnfctl_handle_t * hdl)185 _tnfctl_prbk_buffer_dealloc(tnfctl_handle_t *hdl)
186 {
187 	tifiocstate_t bufstat;
188 	tnfctl_errcode_t prexstat;
189 	int saved_val;
190 
191 	if (ioctl(hdl->kfd, TIFIOCGSTATE, &bufstat) < 0) {
192 		return (tnfctl_status_map(errno));
193 	}
194 	if (bufstat.buffer_state == TIFIOCBUF_NONE) {
195 		return (TNFCTL_ERR_NOBUF);
196 	}
197 
198 	if (bufstat.buffer_state == TIFIOCBUF_OK && !bufstat.trace_stopped) {
199 		return (TNFCTL_ERR_BADDEALLOC);
200 	}
201 	if (ioctl(hdl->kfd, TIFIOCDEALLOCBUF) < 0) {
202 		saved_val = errno;
203 		(void) prbk_get_buf_attrs(hdl);
204 		return (tnfctl_status_map(saved_val));
205 	}
206 
207 	prexstat = prbk_get_buf_attrs(hdl);
208 	if (prexstat)
209 		return (prexstat);
210 
211 	return (TNFCTL_ERR_NONE);
212 }
213 
214 /*
215  * Turns kernel global tracing on or off.
216  */
217 tnfctl_errcode_t
_tnfctl_prbk_set_tracing(tnfctl_handle_t * hdl,boolean_t onoff)218 _tnfctl_prbk_set_tracing(tnfctl_handle_t *hdl, boolean_t onoff)
219 {
220 	if (hdl->trace_state != onoff &&
221 	    ioctl(hdl->kfd, TIFIOCSTRACING, onoff) < 0) {
222 		if (errno == ENOMEM && onoff)
223 			return (TNFCTL_ERR_NOBUF);
224 		else
225 			return (tnfctl_status_map(errno));
226 	}
227 	hdl->trace_state = onoff;
228 	return (TNFCTL_ERR_NONE);
229 }
230 
231 /*
232  * Turn process filter mode on or off.  The process filter is maintained
233  * even when process filtering is off, but has no effect:  all processes
234  * are traced.
235  */
236 tnfctl_errcode_t
_tnfctl_prbk_set_pfilter_mode(tnfctl_handle_t * hdl,boolean_t onoff)237 _tnfctl_prbk_set_pfilter_mode(tnfctl_handle_t *hdl, boolean_t onoff)
238 {
239 	if (hdl->kpidfilter_state != onoff &&
240 	    ioctl(hdl->kfd, TIFIOCSPIDFILTER, onoff) < 0) {
241 		return (tnfctl_status_map(errno));
242 	}
243 	hdl->kpidfilter_state = onoff;
244 	return (TNFCTL_ERR_NONE);
245 }
246 
247 /*
248  * Return the process filter list.
249  */
250 tnfctl_errcode_t
_tnfctl_prbk_get_pfilter_list(tnfctl_handle_t * hdl,pid_t ** ret_list_p,int * ret_count)251 _tnfctl_prbk_get_pfilter_list(tnfctl_handle_t *hdl, pid_t **ret_list_p,
252 				int *ret_count)
253 {
254 	tifiocstate_t kstate;
255 	int *filterset;
256 	int i;
257 	pid_t *ret_list;
258 
259 	if (ioctl(hdl->kfd, TIFIOCGSTATE, &kstate) < 0)
260 		return (tnfctl_status_map(errno));
261 
262 	if (kstate.pidfilter_size == 0) {
263 		*ret_count = 0;
264 		*ret_list_p = NULL;
265 		return (TNFCTL_ERR_NONE);
266 	}
267 
268 	filterset = (int *) malloc((kstate.pidfilter_size + 1) *
269 					sizeof (pid_t));
270 	if (filterset == NULL)
271 		return (TNFCTL_ERR_ALLOCFAIL);
272 	if (ioctl(hdl->kfd, TIFIOCPIDFILTERGET, filterset) < 0)
273 		return (tnfctl_status_map(errno));
274 
275 	/* filterset[0] contains size of array */
276 	ret_list = malloc(filterset[0] * sizeof (pid_t));
277 	if (ret_list == NULL)
278 		return (TNFCTL_ERR_ALLOCFAIL);
279 
280 	for (i = 1; i <= filterset[0]; ++i)
281 		ret_list[i - 1] = filterset[i];
282 
283 	*ret_count = filterset[0];
284 	(void) free(filterset);
285 	*ret_list_p = ret_list;
286 	return (TNFCTL_ERR_NONE);
287 }
288 
289 /*
290  * Add the pid to the process filter list.
291  * check whether it's already in the filter list,
292  * and whether the process exists.
293  */
294 tnfctl_errcode_t
_tnfctl_prbk_pfilter_add(tnfctl_handle_t * hdl,pid_t pid_to_add)295 _tnfctl_prbk_pfilter_add(tnfctl_handle_t *hdl, pid_t pid_to_add)
296 {
297 	if (ioctl(hdl->kfd, TIFIOCSPIDON, pid_to_add) < 0) {
298 		return (tnfctl_status_map(errno));
299 	}
300 	return (TNFCTL_ERR_NONE);
301 }
302 
303 /*
304  * Drop the pid from the process filter list.
305  */
306 tnfctl_errcode_t
_tnfctl_prbk_pfilter_delete(tnfctl_handle_t * hdl,pid_t pid_to_del)307 _tnfctl_prbk_pfilter_delete(tnfctl_handle_t *hdl, pid_t pid_to_del)
308 {
309 	if (ioctl(hdl->kfd, TIFIOCSPIDOFF, pid_to_del) < 0) {
310 		if (errno == ESRCH) {
311 			return (TNFCTL_ERR_NOPROCESS);
312 		} else {
313 			return (tnfctl_status_map(errno));
314 		}
315 	}
316 	return (TNFCTL_ERR_NONE);
317 }
318 
319 /*
320  * get the buffer attributes - side effect tnfctl handle
321  */
322 static tnfctl_errcode_t
prbk_get_buf_attrs(tnfctl_handle_t * hdl)323 prbk_get_buf_attrs(tnfctl_handle_t *hdl)
324 {
325 	tifiocstate_t bufstat;
326 
327 	if (ioctl(hdl->kfd, TIFIOCGSTATE, &bufstat) < 0) {
328 		return (tnfctl_status_map(errno));
329 	}
330 
331 	hdl->trace_file_name = NULL;
332 	hdl->trace_buf_size = bufstat.buffer_size;
333 	if (bufstat.buffer_state == TIFIOCBUF_NONE)
334 		hdl->trace_buf_state = TNFCTL_BUF_NONE;
335 	else if (bufstat.buffer_state == TIFIOCBUF_BROKEN)
336 		hdl->trace_buf_state = TNFCTL_BUF_BROKEN;
337 	else
338 		hdl->trace_buf_state = TNFCTL_BUF_OK;
339 	return (TNFCTL_ERR_NONE);
340 }
341 
342 /*
343  * "Flush" a probe:  i.e., sync up the kernel state with the
344  * (desired) state stored in our data structure.
345  */
346 tnfctl_errcode_t
_tnfctl_prbk_flush(tnfctl_handle_t * hndl,prbctlref_t * p)347 _tnfctl_prbk_flush(tnfctl_handle_t *hndl, prbctlref_t *p)
348 {
349 	tnf_probevals_t probebuf;
350 
351 	probebuf.probenum = p->probe_id;
352 	probebuf.enabled = (p->wrkprbctl.test_func != NULL);
353 	probebuf.traced = (p->wrkprbctl.commit_func == PRBK_DUMMY_COMMIT);
354 	if (ioctl(hndl->kfd, TIFIOCSPROBEVALS, &probebuf) < 0)
355 		return (tnfctl_status_map(errno));
356 	return (TNFCTL_ERR_NONE);
357 }
358 
359 /*
360  * Refresh our understanding of the existing probes in the kernel.
361  */
362 tnfctl_errcode_t
_tnfctl_refresh_kernel(tnfctl_handle_t * hndl)363 _tnfctl_refresh_kernel(tnfctl_handle_t *hndl)
364 {
365 	int maxprobe, i;
366 	int pos;
367 	tnfctl_errcode_t prexstat;
368 	tnf_probevals_t probebuf;
369 	objlist_t *obj_p;
370 	prbctlref_t *p = NULL;
371 
372 	prexstat = prbk_get_buf_attrs(hndl);
373 	if (prexstat)
374 		return (prexstat);
375 	/*
376 	 * Here is where you'd set obj_p->new to B_FALSE and obj_p->old to
377 	 * B_TRUE for all existing objects.  We currently don't need
378 	 * it until we get modload/unload working correctly with probes
379 	 */
380 	if (ioctl(hndl->kfd, TIFIOCGMAXPROBE, &maxprobe) < 0)
381 		return (tnfctl_status_map(errno));
382 	if (maxprobe == hndl->num_probes) {
383 		/* XXX Inadequate in the presence of module unloading */
384 		return (TNFCTL_ERR_NONE);
385 	}
386 
387 	prexstat = alloc_probe_space(hndl, maxprobe);
388 	if (prexstat)
389 		return (prexstat);
390 
391 	obj_p = hndl->objlist;
392 	assert((obj_p != NULL) && (obj_p->probes != NULL));
393 
394 	for (i = 1; i <= maxprobe; ++i) {
395 
396 		if (i >= (obj_p->min_probe_num + obj_p->probecnt)) {
397 			obj_p = obj_p->next;
398 		}
399 
400 		/* make sure we are in the correct object */
401 		assert(obj_p != NULL);
402 		assert((i >= obj_p->min_probe_num) &&
403 			(i < (obj_p->min_probe_num + obj_p->probecnt)));
404 
405 		/* get a pointer to correct probe */
406 		pos = i - obj_p->min_probe_num;
407 		p = &(obj_p->probes[pos]);
408 		assert((p != NULL) && (p->probe_id == i) && (p->probe_handle));
409 
410 		probebuf.probenum = i;
411 		if (ioctl(hndl->kfd, TIFIOCGPROBEVALS, &probebuf) < 0) {
412 			if (errno == ENOENT) {
413 				/*
414 				 * This probe has vanished due to a module
415 				 * unload.
416 				 */
417 				p->probe_handle->valid = B_FALSE;
418 			} else {
419 				return (tnfctl_status_map(errno));
420 			}
421 		} else {
422 			if (p->probe_handle->valid == B_FALSE) {
423 				/*
424 				 * seeing this probe for the first time
425 				 * (alloc_probe_space() initialized this
426 				 * "valid" field to B_FALSE)
427 				 */
428 				/* Update our info about this probe */
429 				p->wrkprbctl.test_func = (probebuf.enabled) ?
430 					PRBK_DUMMY_TEST : NULL;
431 				p->wrkprbctl.commit_func = (probebuf.traced) ?
432 					PRBK_DUMMY_COMMIT : PRBK_DUMMY_ROLLBACK;
433 				p->probe_handle->valid = B_TRUE;
434 				if (probebuf.attrsize < sizeof (probebuf))
435 					probebuf.attrsize = sizeof (probebuf);
436 				p->attr_string = malloc(probebuf.attrsize);
437 				if (p->attr_string == NULL)
438 					return (TNFCTL_ERR_ALLOCFAIL);
439 				/*
440 				 * NOTE: the next statement is a structure
441 				 * copy and *not* a pointer assignment
442 				 */
443 /* LINTED pointer cast may result in improper alignment */
444 				*(tnf_probevals_t *) p->attr_string = probebuf;
445 				if (ioctl(hndl->kfd, TIFIOCGPROBESTRING,
446 						p->attr_string) < 0)
447 					return (tnfctl_status_map(errno));
448 				if (hndl->create_func) {
449 				    p->probe_handle->client_registered_data =
450 					hndl->create_func(hndl,
451 						p->probe_handle);
452 				}
453 			}
454 		}
455 	}
456 	hndl->num_probes = maxprobe;
457 	return (TNFCTL_ERR_NONE);
458 }
459 
460 /*
461  * check if there are any new probes in the kernel that we aren't aware of.
462  * If so, allocate space for those probes in our data structure.
463  */
464 static tnfctl_errcode_t
alloc_probe_space(tnfctl_handle_t * hndl,int maxprobe)465 alloc_probe_space(tnfctl_handle_t *hndl, int maxprobe)
466 {
467 	objlist_t **o_pp;
468 	objlist_t *obj_p, *nobj_p;
469 	int min_probe_num, i;
470 	prbctlref_t *probe_p;
471 
472 	/* we know that: hndl->maxprobe != maxprobe */
473 	obj_p = hndl->objlist;
474 	if (obj_p == NULL) {
475 		/* no objects allocated */
476 		o_pp = &(hndl->objlist);
477 		min_probe_num = 1;
478 	} else {
479 		/* find last object */
480 		while (obj_p->next != NULL) {
481 			/* reset new_probe field on modload/unload */
482 			obj_p->new_probe = B_FALSE;
483 			obj_p = obj_p->next;
484 		}
485 		o_pp = &(obj_p->next);
486 		min_probe_num = obj_p->min_probe_num + obj_p->probecnt;
487 	}
488 
489 	nobj_p = calloc(1, sizeof (objlist_t));
490 	if (nobj_p == NULL)
491 		return (TNFCTL_ERR_ALLOCFAIL);
492 	/* add to the linked list */
493 	*o_pp = nobj_p;
494 	/* NULL, B_FALSE, or 0's not explicitly initialized */
495 	nobj_p->new_probe = B_TRUE;
496 	nobj_p->new = B_TRUE;
497 	nobj_p->objfd = -1;
498 	nobj_p->min_probe_num = min_probe_num;
499 	nobj_p->probecnt = maxprobe - min_probe_num + 1;
500 	nobj_p->probes = calloc(nobj_p->probecnt,  sizeof (prbctlref_t));
501 	if (nobj_p->probes == NULL) {
502 		free(nobj_p);
503 		return (TNFCTL_ERR_ALLOCFAIL);
504 	}
505 
506 	probe_p = &(nobj_p->probes[0]);
507 	for (i = min_probe_num; i <= maxprobe; i++) {
508 		probe_p->obj = nobj_p;
509 		probe_p->probe_id = i;
510 		probe_p->probe_handle = calloc(1, sizeof (tnfctl_probe_t));
511 		if (probe_p->probe_handle == NULL) {
512 			if (nobj_p->probes)
513 				free(nobj_p->probes);
514 			free(nobj_p);
515 			return (TNFCTL_ERR_ALLOCFAIL);
516 		}
517 		probe_p->probe_handle->valid = B_FALSE;
518 		probe_p->probe_handle->probe_p = probe_p;
519 		/* link in probe handle into chain off tnfctl_handle_t */
520 		probe_p->probe_handle->next = hndl->probe_handle_list_head;
521 		hndl->probe_handle_list_head = probe_p->probe_handle;
522 
523 		probe_p++;
524 	}
525 
526 	hndl->num_probes = maxprobe;
527 	return (TNFCTL_ERR_NONE);
528 }
529