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