]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/wpa/src/utils/trace.c
Update hostapd/wpa_supplicant to 2.8 to fix multiple vulnerabilities.
[FreeBSD/FreeBSD.git] / contrib / wpa / src / utils / trace.c
1 /*
2  * Backtrace debugging
3  * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
4  *
5  * This software may be distributed under the terms of the BSD license.
6  * See README for more details.
7  */
8
9 #ifdef WPA_TRACE_BFD
10 #define _GNU_SOURCE
11 #include <link.h>
12 #endif /* WPA_TRACE_BCD */
13 #include "includes.h"
14
15 #include "common.h"
16 #include "trace.h"
17
18 #ifdef WPA_TRACE
19
20 static struct dl_list active_references =
21 { &active_references, &active_references };
22
23 #ifdef WPA_TRACE_BFD
24 #include <bfd.h>
25
26 #define DMGL_PARAMS      (1 << 0)
27 #define DMGL_ANSI        (1 << 1)
28
29 static char *prg_fname = NULL;
30 static bfd *cached_abfd = NULL;
31 static asymbol **syms = NULL;
32 static unsigned long start_offset;
33 static int start_offset_looked_up;
34
35
36 static int callback(struct dl_phdr_info *info, size_t size, void *data)
37 {
38         /*
39          * dl_iterate_phdr(3):
40          * "The first object visited by callback is the main program."
41          */
42         start_offset = info->dlpi_addr;
43
44         /*
45          * dl_iterate_phdr(3):
46          * "The dl_iterate_phdr() function walks through the list of an
47          *  application's shared objects and calls the function callback
48          *  once for each object, until either all shared objects have
49          *  been processed or callback returns a nonzero value."
50          */
51         return 1;
52 }
53
54
55 static void get_prg_fname(void)
56 {
57         char exe[50], fname[512];
58         int len;
59         os_snprintf(exe, sizeof(exe) - 1, "/proc/%u/exe", getpid());
60         len = readlink(exe, fname, sizeof(fname) - 1);
61         if (len < 0 || len >= (int) sizeof(fname)) {
62                 wpa_printf(MSG_ERROR, "readlink: %s", strerror(errno));
63                 return;
64         }
65         fname[len] = '\0';
66         prg_fname = strdup(fname);
67 }
68
69
70 static bfd * open_bfd(const char *fname)
71 {
72         bfd *abfd;
73         char **matching;
74
75         abfd = bfd_openr(prg_fname, NULL);
76         if (abfd == NULL) {
77                 wpa_printf(MSG_INFO, "bfd_openr failed");
78                 return NULL;
79         }
80
81         if (bfd_check_format(abfd, bfd_archive)) {
82                 wpa_printf(MSG_INFO, "bfd_check_format failed");
83                 bfd_close(abfd);
84                 return NULL;
85         }
86
87         if (!bfd_check_format_matches(abfd, bfd_object, &matching)) {
88                 wpa_printf(MSG_INFO, "bfd_check_format_matches failed");
89                 free(matching);
90                 bfd_close(abfd);
91                 return NULL;
92         }
93
94         return abfd;
95 }
96
97
98 static void read_syms(bfd *abfd)
99 {
100         long storage, symcount;
101         bfd_boolean dynamic = FALSE;
102
103         if (syms)
104                 return;
105
106         if (!(bfd_get_file_flags(abfd) & HAS_SYMS)) {
107                 wpa_printf(MSG_INFO, "No symbols");
108                 return;
109         }
110
111         storage = bfd_get_symtab_upper_bound(abfd);
112         if (storage == 0) {
113                 storage = bfd_get_dynamic_symtab_upper_bound(abfd);
114                 dynamic = TRUE;
115         }
116         if (storage < 0) {
117                 wpa_printf(MSG_INFO, "Unknown symtab upper bound");
118                 return;
119         }
120
121         syms = malloc(storage);
122         if (syms == NULL) {
123                 wpa_printf(MSG_INFO, "Failed to allocate memory for symtab "
124                            "(%ld bytes)", storage);
125                 return;
126         }
127         if (dynamic)
128                 symcount = bfd_canonicalize_dynamic_symtab(abfd, syms);
129         else
130                 symcount = bfd_canonicalize_symtab(abfd, syms);
131         if (symcount < 0) {
132                 wpa_printf(MSG_INFO, "Failed to canonicalize %ssymtab",
133                            dynamic ? "dynamic " : "");
134                 free(syms);
135                 syms = NULL;
136                 return;
137         }
138 }
139
140
141 struct bfd_data {
142         bfd_vma pc;
143         bfd_boolean found;
144         const char *filename;
145         const char *function;
146         unsigned int line;
147 };
148
149
150 static void find_addr_sect(bfd *abfd, asection *section, void *obj)
151 {
152         struct bfd_data *data = obj;
153         bfd_vma vma;
154         bfd_size_type size;
155
156         if (data->found)
157                 return;
158
159         if (!(bfd_get_section_vma(abfd, section)))
160                 return;
161
162         vma = bfd_get_section_vma(abfd, section);
163         if (data->pc < vma)
164                 return;
165
166         size = bfd_get_section_size(section);
167         if (data->pc >= vma + size)
168                 return;
169
170         data->found = bfd_find_nearest_line(abfd, section, syms,
171                                             data->pc - vma,
172                                             &data->filename,
173                                             &data->function,
174                                             &data->line);
175 }
176
177
178 static void wpa_trace_bfd_addr(void *pc)
179 {
180         bfd *abfd = cached_abfd;
181         struct bfd_data data;
182         const char *name;
183         char *aname = NULL;
184         const char *filename;
185
186         if (abfd == NULL)
187                 return;
188
189         data.pc = (bfd_hostptr_t) (pc - start_offset);
190         data.found = FALSE;
191         bfd_map_over_sections(abfd, find_addr_sect, &data);
192
193         if (!data.found)
194                 return;
195
196         do {
197                 if (data.function)
198                         aname = bfd_demangle(abfd, data.function,
199                                              DMGL_ANSI | DMGL_PARAMS);
200                 name = aname ? aname : data.function;
201                 filename = data.filename;
202                 if (filename) {
203                         char *end = os_strrchr(filename, '/');
204                         int i = 0;
205                         while (*filename && *filename == prg_fname[i] &&
206                                filename <= end) {
207                                 filename++;
208                                 i++;
209                         }
210                 }
211                 wpa_printf(MSG_INFO, "     %s() %s:%u",
212                            name, filename, data.line);
213                 free(aname);
214                 aname = NULL;
215
216                 data.found = bfd_find_inliner_info(abfd, &data.filename,
217                                                    &data.function, &data.line);
218         } while (data.found);
219 }
220
221
222 static const char * wpa_trace_bfd_addr2func(void *pc)
223 {
224         bfd *abfd = cached_abfd;
225         struct bfd_data data;
226
227         if (abfd == NULL)
228                 return NULL;
229
230         data.pc = (bfd_hostptr_t) (pc - start_offset);
231         data.found = FALSE;
232         bfd_map_over_sections(abfd, find_addr_sect, &data);
233
234         if (!data.found)
235                 return NULL;
236
237         return data.function;
238 }
239
240
241 static void wpa_trace_bfd_init(void)
242 {
243         if (!prg_fname) {
244                 get_prg_fname();
245                 if (!prg_fname)
246                         return;
247         }
248
249         if (!cached_abfd) {
250                 cached_abfd = open_bfd(prg_fname);
251                 if (!cached_abfd) {
252                         wpa_printf(MSG_INFO, "Failed to open bfd");
253                         return;
254                 }
255         }
256
257         read_syms(cached_abfd);
258         if (!syms) {
259                 wpa_printf(MSG_INFO, "Failed to read symbols");
260                 return;
261         }
262
263         if (!start_offset_looked_up) {
264                 dl_iterate_phdr(callback, NULL);
265                 start_offset_looked_up = 1;
266         }
267 }
268
269
270 void wpa_trace_dump_funcname(const char *title, void *pc)
271 {
272         wpa_printf(MSG_INFO, "WPA_TRACE: %s: %p", title, pc);
273         wpa_trace_bfd_init();
274         wpa_trace_bfd_addr(pc);
275 }
276
277
278 size_t wpa_trace_calling_func(const char *buf[], size_t len)
279 {
280         bfd *abfd;
281         void *btrace_res[WPA_TRACE_LEN];
282         int i, btrace_num;
283         size_t pos = 0;
284
285         if (len == 0)
286                 return 0;
287         if (len > WPA_TRACE_LEN)
288                 len = WPA_TRACE_LEN;
289
290         wpa_trace_bfd_init();
291         abfd = cached_abfd;
292         if (!abfd)
293                 return 0;
294
295         btrace_num = backtrace(btrace_res, len);
296         if (btrace_num < 1)
297                 return 0;
298
299         for (i = 0; i < btrace_num; i++) {
300                 struct bfd_data data;
301
302                 data.pc = (bfd_hostptr_t) (btrace_res[i] - start_offset);
303                 data.found = FALSE;
304                 bfd_map_over_sections(abfd, find_addr_sect, &data);
305
306                 while (data.found) {
307                         if (data.function &&
308                             (pos > 0 ||
309                              os_strcmp(data.function, __func__) != 0)) {
310                                 buf[pos++] = data.function;
311                                 if (pos == len)
312                                         return pos;
313                         }
314
315                         data.found = bfd_find_inliner_info(abfd, &data.filename,
316                                                            &data.function,
317                                                            &data.line);
318                 }
319         }
320
321         return pos;
322 }
323
324 #else /* WPA_TRACE_BFD */
325
326 #define wpa_trace_bfd_init() do { } while (0)
327 #define wpa_trace_bfd_addr(pc) do { } while (0)
328 #define wpa_trace_bfd_addr2func(pc) NULL
329
330 #endif /* WPA_TRACE_BFD */
331
332 void wpa_trace_dump_func(const char *title, void **btrace, int btrace_num)
333 {
334         char **sym;
335         int i;
336         enum { TRACE_HEAD, TRACE_RELEVANT, TRACE_TAIL } state;
337
338         wpa_trace_bfd_init();
339         wpa_printf(MSG_INFO, "WPA_TRACE: %s - START", title);
340         sym = backtrace_symbols(btrace, btrace_num);
341         state = TRACE_HEAD;
342         for (i = 0; i < btrace_num; i++) {
343                 const char *func = wpa_trace_bfd_addr2func(btrace[i]);
344                 if (state == TRACE_HEAD && func &&
345                     (os_strcmp(func, "wpa_trace_add_ref_func") == 0 ||
346                      os_strcmp(func, "wpa_trace_check_ref") == 0 ||
347                      os_strcmp(func, "wpa_trace_show") == 0))
348                         continue;
349                 if (state == TRACE_TAIL && sym && sym[i] &&
350                     os_strstr(sym[i], "__libc_start_main"))
351                         break;
352                 if (state == TRACE_HEAD)
353                         state = TRACE_RELEVANT;
354                 if (sym)
355                         wpa_printf(MSG_INFO, "[%d]: %s", i, sym[i]);
356                 else
357                         wpa_printf(MSG_INFO, "[%d]: ?? [%p]", i, btrace[i]);
358                 wpa_trace_bfd_addr(btrace[i]);
359                 if (state == TRACE_RELEVANT && func &&
360                     os_strcmp(func, "main") == 0)
361                         state = TRACE_TAIL;
362         }
363         free(sym);
364         wpa_printf(MSG_INFO, "WPA_TRACE: %s - END", title);
365 }
366
367
368 void wpa_trace_show(const char *title)
369 {
370         struct info {
371                 WPA_TRACE_INFO
372         } info;
373         wpa_trace_record(&info);
374         wpa_trace_dump(title, &info);
375 }
376
377
378 void wpa_trace_add_ref_func(struct wpa_trace_ref *ref, const void *addr)
379 {
380         if (addr == NULL)
381                 return;
382         ref->addr = addr;
383         wpa_trace_record(ref);
384         dl_list_add(&active_references, &ref->list);
385 }
386
387
388 void wpa_trace_check_ref(const void *addr)
389 {
390         struct wpa_trace_ref *ref;
391         dl_list_for_each(ref, &active_references, struct wpa_trace_ref, list) {
392                 if (addr != ref->addr)
393                         continue;
394                 wpa_trace_show("Freeing referenced memory");
395                 wpa_trace_dump("Reference registration", ref);
396                 abort();
397         }
398 }
399
400
401 void wpa_trace_deinit(void)
402 {
403 #ifdef WPA_TRACE_BFD
404         free(syms);
405         syms = NULL;
406 #endif /* WPA_TRACE_BFD */
407 }
408
409 #endif /* WPA_TRACE */