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
29static	int _dl_traceplt;
30
31struct tracespec {
32	int	inverse;	/* blacklist instead of whitelist */
33	char	*spec;		/* comma separated spec entries */
34};
35
36static struct tracespec _dl_tracelib, _dl_tracefunc;
37
38static const char *_dl_trace_parse_spec(const char *, struct tracespec *);
39static int _dl_trace_match(const char *, struct tracespec *, int);
40
41void
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
82void
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
103int
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 */
121const 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 */
154static 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