xref: /openbsd/libexec/ld.so/trace.c (revision d415bd75)
1 /*	$OpenBSD: trace.c,v 1.5 2022/01/08 06:49:41 guenther Exp $	*/
2 
3 /*
4  * Copyright (c) 2013 Miodrag Vallat.
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/types.h>
20 
21 #include "syscall.h"
22 #include "util.h"
23 #include "resolve.h"
24 
25 /*
26  * Library call tracing routines.
27  */
28 
29 static	int _dl_traceplt;
30 
31 struct tracespec {
32 	int	inverse;	/* blacklist instead of whitelist */
33 	char	*spec;		/* comma separated spec entries */
34 };
35 
36 static struct tracespec _dl_tracelib, _dl_tracefunc;
37 
38 static const char *_dl_trace_parse_spec(const char *, struct tracespec *);
39 static int _dl_trace_match(const char *, struct tracespec *, int);
40 
41 void
42 _dl_trace_setup(char **envp)
43 {
44 	const char *var;
45 	int inherit;
46 
47 	var = _dl_getenv("LD_TRACE_PLT", envp);
48 	if (var == NULL)
49 		return;
50 
51 	if (!_dl_trust) {
52 		_dl_unsetenv("LD_TRACE_PLT", envp);
53 		return;
54 	}
55 
56 	_dl_traceplt = 1;
57 
58 	/*
59 	 * We expect LD_TRACE_PLT to be empty unless trace inheritance has
60 	 * been setup by ltrace(1).  We can then clear the environment
61 	 * variable to avoid useless work in our children, should we fork
62 	 * any.
63 	 */
64 	inherit = *var != '\0';
65 	if (!inherit)
66 		_dl_unsetenv("LD_TRACE_PLT", envp);
67 
68 	/*
69 	 * Check for a fine-grained trace specification, and extract the
70 	 * library and function lists, if any.
71 	 */
72 
73 	var = _dl_getenv("LD_TRACE_PLTSPEC", envp);
74 	if (var != NULL) {
75 		var = _dl_trace_parse_spec(var, &_dl_tracelib);
76 		(void)_dl_trace_parse_spec(var, &_dl_tracefunc);
77 		if (!inherit)
78 			_dl_unsetenv("LD_TRACE_PLTSPEC", envp);
79 	}
80 }
81 
82 void
83 _dl_trace_object_setup(elf_object_t *object)
84 {
85 	const char *basename, *slash;
86 
87 	object->traced = 0;
88 
89 	if (_dl_traceplt) {
90 		basename = object->load_name;
91 		while (*basename == '/') {
92 			basename++;
93 			slash = _dl_strchr(basename, '/');
94 			if (slash == NULL)
95 				break;
96 			basename = slash;
97 		}
98 		if (_dl_trace_match(basename, &_dl_tracelib, 1))
99 			object->traced = 1;
100 	}
101 }
102 
103 int
104 _dl_trace_plt(const elf_object_t *object, const char *symname)
105 {
106 	if (!_dl_trace_match(symname, &_dl_tracefunc, 0))
107 		return 0;
108 
109 	_dl_utrace(".plt object",
110 	    object->load_name, _dl_strlen(object->load_name));
111 	_dl_utrace(".plt symbol",
112 	    symname, _dl_strlen(symname));
113 
114 	return 1;	/* keep tracing */
115 }
116 
117 /*
118  * Extract a trace specification field, and setup the tracespec struct
119  * accordingly.
120  */
121 const char *
122 _dl_trace_parse_spec(const char *var, struct tracespec *spec)
123 {
124 	const char *start, *end;
125 
126 	if (*var == '!') {
127 		spec->inverse = 1;
128 		var++;
129 	}
130 
131 	start = var;
132 	end = _dl_strchr(start, ':');
133 	if (end == NULL)
134 		end = start + _dl_strlen(start);
135 
136 	if (end != start) {
137 		spec->spec = _dl_malloc(1 + end - start);
138 		if (spec->spec == NULL)
139 			_dl_oom();
140 
141 		_dl_bcopy(start, spec->spec, end - start);
142 		spec->spec[end - start] = '\0';
143 	}
144 
145 	if (*end == ':')
146 		end++;
147 
148 	return end;
149 }
150 
151 /*
152  * Check if a given name matches a trace specification list.
153  */
154 static int
155 _dl_trace_match(const char *name, struct tracespec *spec, int allow_so)
156 {
157 	const char *list, *end, *next;
158 	size_t span;
159 	int match;
160 
161 	/* no spec means trace everything */
162 	if (spec->spec == NULL)
163 		return 1;
164 
165 	match = 0;
166 	list = spec->spec;
167 	end = list + _dl_strlen(list);
168 
169 	while (*list != '\0') {
170 		next = _dl_strchr(list, ',');
171 		if (next == NULL)
172 			next = end;
173 
174 		span = next - list;
175 		if (span != 0 && *(next - 1) == '*')
176 			span--;
177 
178 		if (span != 0 && _dl_strncmp(name, list, span) == 0) {
179 			/*
180 			 * If the object name matches the specification
181 			 * fragment so far, it's a match if:
182 			 *   + the specification ends in a star (wildcard
183 			 *     match)
184 			 *   + there are no remaining chars in both the
185 			 *     object name and the specification (exact
186 			 *     match)
187 			 *   + the specification ends (no star) and the
188 			 *     object name continues with ".so" (radix
189 			 *     match) and `allow_so' is nonzero.
190 			 */
191 			if (list[span] == '*' ||
192 			    name[span] == '\0' ||
193 			    (allow_so &&
194 			    _dl_strncmp(name + span, ".so", 3) == 0)) {
195 				match = 1;
196 				break;
197 			}
198 		}
199 
200 		while (*next == ',')
201 			next++;
202 		list = next;
203 	}
204 
205 	return spec->inverse ? !match : match;
206 }
207