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
_dl_trace_setup(char ** envp)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
_dl_trace_object_setup(elf_object_t * object)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
_dl_trace_plt(const elf_object_t * object,const char * symname)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 *
_dl_trace_parse_spec(const char * var,struct tracespec * spec)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
_dl_trace_match(const char * name,struct tracespec * spec,int allow_so)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