asterisk/main/backtrace.c
Corey Farrell ee65d5ac7c ast_bt_get_symbols: Prevent double-free.
It's possible for bfdobj to be created but syms not created.  If syms
was not allocated in the current loop iteration but was allocated in the
previous iteration it would crash.

ASTERISK-27340

Change-Id: I5b110c609f6dfe91339f782a99a431bca5837363
2017-10-13 10:33:04 -05:00

226 lines
5.6 KiB
C

/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 1999 - 2013, Digium, Inc.
*
* Matt Jordan <mjordan@digium.com>
*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
/*! \file
* \brief Asterisk backtrace generation
*
* This file provides backtrace generation utilities
*/
/*** MODULEINFO
<support_level>core</support_level>
***/
#include "asterisk.h"
#include "asterisk/backtrace.h"
#include "asterisk/utils.h"
#include "asterisk/strings.h"
#ifdef HAVE_BKTR
#include <execinfo.h>
#if defined(HAVE_DLADDR) && defined(HAVE_BFD) && defined(BETTER_BACKTRACES)
#include <dlfcn.h>
#include <bfd.h>
#endif
struct ast_bt *__ast_bt_create(void)
{
struct ast_bt *bt = ast_std_calloc(1, sizeof(*bt));
if (!bt) {
return NULL;
}
bt->alloced = 1;
ast_bt_get_addresses(bt);
return bt;
}
int __ast_bt_get_addresses(struct ast_bt *bt)
{
bt->num_frames = backtrace(bt->addresses, AST_MAX_BT_FRAMES);
return 0;
}
void *__ast_bt_destroy(struct ast_bt *bt)
{
if (bt && bt->alloced) {
ast_std_free(bt);
}
return NULL;
}
char **__ast_bt_get_symbols(void **addresses, size_t num_frames)
{
char **strings;
#if defined(BETTER_BACKTRACES)
int stackfr;
bfd *bfdobj; /* bfd.h */
Dl_info dli; /* dlfcn.h */
long allocsize;
asymbol **syms = NULL; /* bfd.h */
bfd_vma offset; /* bfd.h */
const char *lastslash;
asection *section;
const char *file, *func;
unsigned int line;
char address_str[128];
char msg[1024];
size_t strings_size;
size_t *eachlen;
#endif
#if defined(BETTER_BACKTRACES)
strings_size = num_frames * sizeof(*strings);
eachlen = ast_std_calloc(num_frames, sizeof(*eachlen));
strings = ast_std_calloc(num_frames, sizeof(*strings));
if (!eachlen || !strings) {
ast_std_free(eachlen);
ast_std_free(strings);
return NULL;
}
for (stackfr = 0; stackfr < num_frames; stackfr++) {
int found = 0, symbolcount;
msg[0] = '\0';
if (!dladdr(addresses[stackfr], &dli)) {
continue;
}
if (strcmp(dli.dli_fname, "asterisk") == 0) {
char asteriskpath[256];
if (!(dli.dli_fname = ast_utils_which("asterisk", asteriskpath, sizeof(asteriskpath)))) {
/* This will fail to find symbols */
dli.dli_fname = "asterisk";
}
}
lastslash = strrchr(dli.dli_fname, '/');
if ((bfdobj = bfd_openr(dli.dli_fname, NULL)) &&
bfd_check_format(bfdobj, bfd_object) &&
(allocsize = bfd_get_symtab_upper_bound(bfdobj)) > 0 &&
(syms = ast_std_malloc(allocsize)) &&
(symbolcount = bfd_canonicalize_symtab(bfdobj, syms))) {
if (bfdobj->flags & DYNAMIC) {
offset = addresses[stackfr] - dli.dli_fbase;
} else {
offset = addresses[stackfr] - (void *) 0;
}
for (section = bfdobj->sections; section; section = section->next) {
if (!bfd_get_section_flags(bfdobj, section) & SEC_ALLOC ||
section->vma > offset ||
section->size + section->vma < offset) {
continue;
}
if (!bfd_find_nearest_line(bfdobj, section, syms, offset - section->vma, &file, &func, &line)) {
continue;
}
/* file can possibly be null even with a success result from bfd_find_nearest_line */
file = file ? file : "";
/* Stack trace output */
found++;
if ((lastslash = strrchr(file, '/'))) {
const char *prevslash;
for (prevslash = lastslash - 1; *prevslash != '/' && prevslash >= file; prevslash--) {
}
if (prevslash >= file) {
lastslash = prevslash;
}
}
if (dli.dli_saddr == NULL) {
address_str[0] = '\0';
} else {
snprintf(address_str, sizeof(address_str), " (%p+%lX)",
dli.dli_saddr,
(unsigned long) (addresses[stackfr] - dli.dli_saddr));
}
snprintf(msg, sizeof(msg), "%s:%u %s()%s",
lastslash ? lastslash + 1 : file, line,
S_OR(func, "???"),
address_str);
break; /* out of section iteration */
}
}
if (bfdobj) {
bfd_close(bfdobj);
ast_std_free(syms);
syms = NULL;
}
/* Default output, if we cannot find the information within BFD */
if (!found) {
if (dli.dli_saddr == NULL) {
address_str[0] = '\0';
} else {
snprintf(address_str, sizeof(address_str), " (%p+%lX)",
dli.dli_saddr,
(unsigned long) (addresses[stackfr] - dli.dli_saddr));
}
snprintf(msg, sizeof(msg), "%s %s()%s",
lastslash ? lastslash + 1 : dli.dli_fname,
S_OR(dli.dli_sname, "<unknown>"),
address_str);
}
if (!ast_strlen_zero(msg)) {
char **tmp;
eachlen[stackfr] = strlen(msg) + 1;
if (!(tmp = ast_std_realloc(strings, strings_size + eachlen[stackfr]))) {
ast_std_free(strings);
strings = NULL;
break; /* out of stack frame iteration */
}
strings = tmp;
strings[stackfr] = (char *) strings + strings_size;
strcpy(strings[stackfr], msg);/* Safe since we just allocated the room. */
strings_size += eachlen[stackfr];
}
}
if (strings) {
/* Recalculate the offset pointers because of the reallocs. */
strings[0] = (char *) strings + num_frames * sizeof(*strings);
for (stackfr = 1; stackfr < num_frames; stackfr++) {
strings[stackfr] = strings[stackfr - 1] + eachlen[stackfr - 1];
}
}
ast_std_free(eachlen);
#else /* !defined(BETTER_BACKTRACES) */
strings = backtrace_symbols(addresses, num_frames);
#endif /* defined(BETTER_BACKTRACES) */
return strings;
}
#endif /* HAVE_BKTR */