Coverage Report

Created: 2025-07-11 06:58

/src/dovecot/src/lib/backtrace-string.c
Line
Count
Source (jump to first uncovered line)
1
/* Copyright (c) 2006-2018 Dovecot authors, see the included COPYING file */
2
3
#include "lib.h"
4
#include "str.h"
5
#include "backtrace-string.h"
6
7
#define MAX_STACK_SIZE 30
8
#define BACKTRACE_SKIP_PREFIX "backtrace_"
9
10
#if defined(HAVE_LIBUNWIND)
11
12
#include <libunwind.h>
13
14
static int backtrace_append_unwind(string_t *str, const char **error_r)
15
{
16
  size_t str_orig_size = str_len(str);
17
  char proc_name[256];
18
  int ret;
19
  unsigned int fp = 0;
20
  unw_cursor_t c;
21
  unw_context_t ctx;
22
  unw_proc_info_t pip;
23
  bool success = FALSE;
24
25
  if ((ret = unw_getcontext(&ctx)) != 0) {
26
    *error_r = t_strdup_printf("unw_getcontext() failed: %d", ret);
27
    return -1;
28
  }
29
  if ((ret = unw_init_local(&c, &ctx)) != 0) {
30
    *error_r = t_strdup_printf("unw_init_local() failed: %d", ret);
31
    return -1;
32
  }
33
34
  do {
35
    str_printfa(str, "#%d ", fp);
36
    if ((ret = unw_get_proc_info(&c, &pip)) != 0) {
37
      str_printfa(str, "[unw_get_proc_info_failed(): %d]", ret);
38
    } else if (pip.start_ip == 0 || pip.end_ip == 0) {
39
      str_append(str, "[no start/end information]");
40
    } else if ((ret = unw_get_proc_name(&c, proc_name, sizeof(proc_name), 0)) != 0 &&
41
         ret != UNW_ENOMEM) {
42
      str_printfa(str, "[unw_get_proc_name() failed: %d]", ret);
43
    } else if (!success && str_begins_with(proc_name, BACKTRACE_SKIP_PREFIX)) {
44
      str_truncate(str, str_orig_size);
45
      continue;
46
    } else {
47
      str_append_max(str, proc_name, sizeof(proc_name));
48
      str_printfa(str, "[0x%08zx]", pip.start_ip);
49
      success = TRUE;
50
    }
51
    str_append(str, " -> ");
52
    fp++;
53
  } while ((ret = unw_step(&c)) > 0);
54
55
  /* remove ' -> ' */
56
  if (str->used > 4)
57
    str_truncate(str, str->used - 4);
58
  if (ret < 0) {
59
    *error_r = t_strdup_printf("unw_step() failed: %d", ret);
60
    return -1;
61
  }
62
  if (!success) {
63
    *error_r = t_strdup_printf("No symbols found (process chrooted?)");
64
    return -1;
65
  }
66
  return 0;
67
}
68
#endif
69
70
#if defined(HAVE_BACKTRACE_SYMBOLS) && defined(HAVE_EXECINFO_H)
71
/* Linux */
72
#include <execinfo.h>
73
74
static int backtrace_append_libc(string_t *str, const char **error_r)
75
0
{
76
0
  size_t str_orig_size = str_len(str);
77
0
  void *stack[MAX_STACK_SIZE];
78
0
  char **strings;
79
0
  int ret, i;
80
81
0
  ret = backtrace(stack, N_ELEMENTS(stack));
82
0
  if (ret <= 0) {
83
0
    *error_r = "backtrace() failed";
84
0
    return -1;
85
0
  }
86
87
0
  strings = backtrace_symbols(stack, ret);
88
0
  if (strings == NULL) {
89
0
    *error_r = "backtrace_symbols() failed";
90
0
    return -1;
91
0
  }
92
0
  for (i = 0; i < ret; i++) {
93
0
    if (str_len(str) > str_orig_size)
94
0
      str_append(str, " -> ");
95
96
0
    if (str_len(str) != str_orig_size ||
97
0
        !str_begins_with(strings[i], BACKTRACE_SKIP_PREFIX)) {
98
      /* String often contains a full path to the binary,
99
         followed by the function name. The path causes the
100
         backtrace to be excessively large and we don't
101
         especially care about it, so just skip over it. */
102
0
      const char *suffix = strrchr(strings[i], '/');
103
0
      if (suffix != NULL)
104
0
        suffix++;
105
0
      else
106
0
        suffix = strings[i];
107
0
      str_append(str, suffix);
108
0
    }
109
0
  }
110
0
  free(strings);
111
0
  return 0;
112
0
}
113
#elif defined(HAVE_WALKCONTEXT) && defined(HAVE_UCONTEXT_H)
114
/* Solaris */
115
#include <ucontext.h>
116
117
struct walk_context {
118
  string_t *str;
119
  unsigned int pos;
120
};
121
122
static int walk_callback(uintptr_t ptr, int signo ATTR_UNUSED,
123
       void *context)
124
{
125
  struct walk_context *ctx = context;
126
127
  if (ctx->pos > 0)
128
    str_append(ctx->str, " -> ");
129
  str_printfa(ctx->str, "0x%p", (void *)ptr);
130
  ctx->pos++;
131
  return 0;
132
}
133
134
static int backtrace_append_libc(string_t *str, const char **error_r)
135
{
136
  ucontext_t uc;
137
  struct walk_context ctx;
138
139
  if (getcontext(&uc) < 0) {
140
    *error_r = t_strdup_printf("getcontext() failed: %m");
141
    return -1;
142
  }
143
144
  ctx.str = str;
145
  ctx.pos = 0;
146
  walkcontext(&uc, walk_callback, &ctx);
147
  return 0;
148
}
149
#else
150
static int
151
backtrace_append_libc(string_t *str ATTR_UNUSED, const char **error_r)
152
{
153
  *error_r = "Missing implementation";
154
  return -1;
155
}
156
#endif
157
158
int backtrace_append(string_t *str, const char **error_r)
159
0
{
160
#if defined(HAVE_LIBUNWIND)
161
  size_t orig_len = str_len(str);
162
  if (backtrace_append_unwind(str, error_r) == 0)
163
    return 0;
164
  /* failed to get useful backtrace. libc's own method is likely
165
     better. */
166
  str_truncate(str, orig_len);
167
#endif
168
0
  return backtrace_append_libc(str, error_r);
169
0
}
170
171
int backtrace_get(const char **backtrace_r, const char **error_r)
172
0
{
173
0
  string_t *str;
174
175
0
  str = t_str_new(512);
176
0
  if (backtrace_append(str, error_r) < 0)
177
0
    return -1;
178
179
0
  *backtrace_r = str_c(str);
180
0
  return 0;
181
0
}