overload libc memory allocators with custom ones to trace all allocations (#13810)
* overload libc memory allocators with custom ones to trace all allocations * grab libc pointers for external c plugins * use -ldl when necessary; fallback to work without dlsym when it is not available * initialize global variable * add optional dl libs * dynamically link every library function when needed for the first time * prevent crashes on musl libc * another attempt * dont dereference function * attempt no 3 * attempt no 4 * cleanup - all attempts failed * dont enable tracing of allocations * missing parenthesis
Costa Tsaousis committed
Oct 13, 2022 at 08:04 UTC
2974f525ec703329ef6ad079d8f6c685cfab11ad
12 files changed
+197
-24
Makefile.am
+1
@@ -964,6 +964,7 @@ NETDATA_COMMON_LIBS = \
964
$(OPTIONAL_SSL_LIBS) \
965
$(OPTIONAL_JSONC_LIBS) \
966
$(OPTIONAL_ATOMIC_LIBS) \
967
+ $(OPTIONAL_DL_LIBS) \
968
$(NULL)
969
970
if LINK_STATIC_JSONC
collectors/apps.plugin/apps_plugin.c
+1
-1
@@ -2935,7 +2935,7 @@ static size_t zero_all_targets(struct target *root) {
2935
while(pid_on_target) {
2936
pid_on_target_to_free = pid_on_target;
2937
pid_on_target = pid_on_target->next;
2938
- free(pid_on_target_to_free);
2938
+ freez(pid_on_target_to_free);
2939
}
2940
2941
w->root_pid = NULL;
configure.ac
+26
@@ -269,6 +269,8 @@ if test "${enable_accept4}" != "no"; then
269
AC_CHECK_FUNCS_ONCE(accept4)
270
fi
271
272
+AC_CHECK_FUNCS_ONCE(malloc_usable_size)
273
+
274
# -----------------------------------------------------------------------------
275
# operating system detection
276
@@ -1529,6 +1531,30 @@ AC_SUBST([OPTIONAL_ATOMIC_LIBS])
1531
1532
AC_LANG_POP([C++])
1533
1534
+# -----------------------------------------------------------------------------
1535
+
1536
+AC_MSG_CHECKING(whether we can use dlsym)
1537
+OLD_LIBS="${LIBS}"
1538
+LIBS="-ldl"
1539
+AC_LINK_IFELSE([AC_LANG_SOURCE([[
1540
+ #include <dlfcn.h>
1541
+ static void *(*libc_malloc)(size_t);
1542
+ int main() {
1543
+ libc_malloc = dlsym(RTLD_NEXT, "malloc");
1544
+ }
1545
+]])], CAN_USE_DLSYM=yes, CAN_USE_DLSYM=no)
1546
+LIBS="${OLD_LIBS}"
1547
+AC_MSG_RESULT($CAN_USE_DLSYM)
1548
+
1549
+if test "x$CAN_USE_DLSYM" = xyes; then
1550
+ AC_DEFINE([HAVE_DLSYM], [1], [dlsym usability])
1551
+ OPTIONAL_DL_LIBS="-ldl"
1552
+fi
1553
+AC_SUBST([OPTIONAL_DL_LIBS])
1554
+
1555
+# -----------------------------------------------------------------------------
1556
+
1557
+
1558
AC_DEFINE_UNQUOTED([NETDATA_USER], ["${with_user}"], [use this user to drop privileged])
1559
1560
varlibdir="${localstatedir}/lib/netdata"
daemon/main.c
+6
@@ -1317,6 +1317,12 @@ int main(int argc, char **argv) {
1317
i = (int)config_get_number(CONFIG_SECTION_GLOBAL, "glibc malloc arena max for netdata", 1);
1318
if(i > 0)
1319
mallopt(M_ARENA_MAX, 1);
1320
+
1321
+
1322
+#ifdef NETDATA_INTERNAL_CHECKS
1323
+ mallopt(M_PERTURB, 0x5A);
1324
+ // mallopt(M_MXFAST, 0);
1325
+#endif
1326
#endif
1327
1328
// initialize the system clocks
database/engine/datafile.c
+2
-2
@@ -174,7 +174,7 @@ int create_data_file(struct rrdengine_datafile *datafile)
174
rrd_stat_atomic_add(&global_io_errors, 1);
175
}
176
uv_fs_req_cleanup(&req);
177
- free(superblock);
177
+ posix_memfree(superblock);
178
if (ret < 0) {
179
destroy_data_file(datafile);
180
return ret;
@@ -218,7 +218,7 @@ static int check_data_file_superblock(uv_file file)
218
ret = 0;
219
}
220
error:
221
- free(superblock);
221
+ posix_memfree(superblock);
222
return ret;
223
}
224
database/engine/journalfile.c
+4
-4
@@ -17,7 +17,7 @@ static void flush_transaction_buffer_cb(uv_fs_t* req)
17
}
18
19
uv_fs_req_cleanup(req);
20
- free(io_descr->buf);
20
+ posix_memfree(io_descr->buf);
21
freez(io_descr);
22
}
23
@@ -225,7 +225,7 @@ int create_journal_file(struct rrdengine_journalfile *journalfile, struct rrdeng
225
rrd_stat_atomic_add(&global_io_errors, 1);
226
}
227
uv_fs_req_cleanup(&req);
228
- free(superblock);
228
+ posix_memfree(superblock);
229
if (ret < 0) {
230
destroy_journal_file(journalfile, datafile);
231
return ret;
@@ -268,7 +268,7 @@ static int check_journal_file_superblock(uv_file file)
268
ret = 0;
269
}
270
error:
271
- free(superblock);
271
+ posix_memfree(superblock);
272
return ret;
273
}
274
@@ -483,7 +483,7 @@ static uint64_t iterate_transactions(struct rrdengine_instance *ctx, struct rrde
483
}
484
skip_file:
485
if (unlikely(!journal_is_mmapped))
486
- free(buf);
486
+ posix_memfree(buf);
487
return max_id;
488
}
489
database/engine/metadata_log/logfile.c
+1
-1
@@ -103,7 +103,7 @@ static int check_metadata_logfile_superblock(uv_file file)
103
error("File has unknown version %"PRIu16". Compatibility is not guaranteed.", superblock->version);
104
}
105
error:
106
- free(superblock);
106
+ posix_memfree(superblock);
107
return ret;
108
}
109
database/engine/rrdengine.c
+2
-2
@@ -422,7 +422,7 @@ after_crc_check:
422
if (xt_io_descr->completion)
423
completion_mark_complete(xt_io_descr->completion);
424
uv_fs_req_cleanup(req);
425
- free(xt_io_descr->buf);
425
+ posix_memfree(xt_io_descr->buf);
426
freez(xt_io_descr);
427
}
428
@@ -696,7 +696,7 @@ void flush_pages_cb(uv_fs_t* req)
696
if (xt_io_descr->completion)
697
completion_mark_complete(xt_io_descr->completion);
698
uv_fs_req_cleanup(req);
699
- free(xt_io_descr->buf);
699
+ posix_memfree(xt_io_descr->buf);
700
freez(xt_io_descr);
701
702
uv_rwlock_wrlock(&pg_cache->committed_page_index.lock);
libnetdata/libnetdata.c
+144
-9
@@ -34,6 +34,123 @@ const char *program_version = VERSION;
34
#warning NETDATA_TRACE_ALLOCATIONS ENABLED
35
#include "Judy.h"
36
37
+#ifdef HAVE_DLSYM
38
+#include <dlfcn.h>
39
+
40
+typedef void (*libc_function_t)(void);
41
+
42
+static void *malloc_first_run(size_t size);
43
+static void *(*libc_malloc)(size_t) = malloc_first_run;
44
+
45
+static void *calloc_first_run(size_t n, size_t size);
46
+static void *(*libc_calloc)(size_t, size_t) = calloc_first_run;
47
+
48
+static void *realloc_first_run(void *ptr, size_t size);
49
+static void *(*libc_realloc)(void *, size_t) = realloc_first_run;
50
+
51
+static void free_first_run(void *ptr);
52
+static void (*libc_free)(void *) = free_first_run;
53
+
54
+static char *strdup_first_run(const char *s);
55
+static char *(*libc_strdup)(const char *) = strdup_first_run;
56
+
57
+static size_t malloc_usable_size_first_run(void *ptr);
58
+#ifdef HAVE_MALLOC_USABLE_SIZE
59
+static size_t (*libc_malloc_usable_size)(void *) = malloc_usable_size_first_run;
60
+#else
61
+static size_t (*libc_malloc_usable_size)(void *) = NULL;
62
+#endif
63
+
64
+static void link_system_library_function(libc_function_t *func_pptr, const char *name, bool required) {
65
+ *func_pptr = dlsym(RTLD_NEXT, name);
66
+ if(!*func_pptr && required) {
67
+ fprintf(stderr, "FATAL: Cannot find system's %s() function.\n", name);
68
+ abort();
69
+ }
70
+}
71
+
72
+static void *malloc_first_run(size_t size) {
73
+ link_system_library_function((libc_function_t *) &libc_malloc, "malloc", true);
74
+ return libc_malloc(size);
75
+}
76
+
77
+static void *calloc_first_run(size_t n, size_t size) {
78
+ link_system_library_function((libc_function_t *) &libc_calloc, "calloc", true);
79
+ return libc_calloc(n, size);
80
+}
81
+
82
+static void *realloc_first_run(void *ptr, size_t size) {
83
+ link_system_library_function((libc_function_t *) &libc_realloc, "realloc", true);
84
+ return libc_realloc(ptr, size);
85
+}
86
+
87
+static void free_first_run(void *ptr) {
88
+ link_system_library_function((libc_function_t *) &libc_free, "free", true);
89
+ libc_free(ptr);
90
+}
91
+
92
+static char *strdup_first_run(const char *s) {
93
+ link_system_library_function((libc_function_t *) &libc_strdup, "strdup", true);
94
+ return libc_strdup(s);
95
+}
96
+
97
+static size_t malloc_usable_size_first_run(void *ptr) {
98
+ link_system_library_function((libc_function_t *) &libc_malloc_usable_size, "malloc_usable_size", false);
99
+
100
+ if(libc_malloc_usable_size)
101
+ return libc_malloc_usable_size(ptr);
102
+ else
103
+ return 0;
104
+}
105
+
106
+void *malloc(size_t size) {
107
+ return mallocz(size);
108
+}
109
+
110
+void *calloc(size_t n, size_t size) {
111
+ return callocz(n, size);
112
+}
113
+
114
+void *realloc(void *ptr, size_t size) {
115
+ return reallocz(ptr, size);
116
+}
117
+
118
+void *reallocarray(void *ptr, size_t n, size_t size) {
119
+ return reallocz(ptr, n * size);
120
+}
121
+
122
+void free(void *ptr) {
123
+ freez(ptr);
124
+}
125
+
126
+char *strdup(const char *s) {
127
+ return strdupz(s);
128
+}
129
+
130
+size_t malloc_usable_size(void *ptr) {
131
+ return mallocz_usable_size(ptr);
132
+}
133
+#else // !HAVE_DLSYM
134
+
135
+static void *(*libc_malloc)(size_t) = malloc;
136
+static void *(*libc_calloc)(size_t, size_t) = calloc;
137
+static void *(*libc_realloc)(void *, size_t) = realloc;
138
+static void (*libc_free)(void *) = free;
139
+static char *(*libc_strdup)(const char *) = strdup;
140
+
141
+#ifdef HAVE_MALLOC_USABLE_SIZE
142
+static size_t (*libc_malloc_usable_size)(void *) = malloc_usable_size;
143
+#else
144
+static size_t (*libc_malloc_usable_size)(void *) = NULL;
145
+#endif
146
+
147
+#endif // HAVE_DLSYM
148
+
149
+
150
+void posix_memfree(void *ptr) {
151
+ libc_free(ptr);
152
+}
153
+
154
Word_t JudyMalloc(Word_t Words) {
155
Word_t Addr;
156
@@ -57,7 +174,7 @@ void JudyFreeVirtual(void * PWord, Word_t Words) {
174
175
#define MALLOC_ALIGNMENT (sizeof(uintptr_t) * 2)
176
#define size_t_atomic_count(op, var, size) __atomic_## op ##_fetch(&(var), size, __ATOMIC_RELAXED)
60
-#define size_t_atomic_bytes(op, var, size) __atomic_## op ##_fetch(&(var), ((size) % MALLOC_ALIGNMENT)?((size) + MALLOC_ALIGNMENT - (size % MALLOC_ALIGNMENT)):(size), __ATOMIC_RELAXED)
177
+#define size_t_atomic_bytes(op, var, size) __atomic_## op ##_fetch(&(var), ((size) % MALLOC_ALIGNMENT)?((size) + MALLOC_ALIGNMENT - ((size) % MALLOC_ALIGNMENT)):(size), __ATOMIC_RELAXED)
178
179
struct malloc_header_signature {
180
uint32_t magic;
@@ -100,7 +217,7 @@ static struct malloc_trace *malloc_trace_find_or_create(const char *file, const
217
218
struct malloc_trace *t = (struct malloc_trace *)avl_search_lock(&malloc_trace_index, (avl_t *)&tmp);
219
if(!t) {
103
- t = calloc(1, sizeof(struct malloc_trace));
220
+ t = libc_calloc(1, sizeof(struct malloc_trace));
221
if(!t) fatal("No memory");
222
t->line = line;
223
t->function = function;
@@ -140,7 +257,7 @@ void *mallocz_int(size_t size, const char *file, const char *function, size_t li
257
size_t_atomic_count(add, p->allocations, 1);
258
size_t_atomic_bytes(add, p->bytes, size);
259
143
- struct malloc_header *t = (struct malloc_header *)malloc(malloc_header_size + size);
260
+ struct malloc_header *t = (struct malloc_header *)libc_malloc(malloc_header_size + size);
261
if (unlikely(!t)) fatal("mallocz() cannot allocate %zu bytes of memory (%zu with header).", size, malloc_header_size + size);
262
t->signature.magic = 0x0BADCAFE;
263
t->signature.trace = p;
@@ -162,7 +279,7 @@ void *callocz_int(size_t nmemb, size_t size, const char *file, const char *funct
279
size_t_atomic_count(add, p->allocations, 1);
280
size_t_atomic_bytes(add, p->bytes, size);
281
165
- struct malloc_header *t = (struct malloc_header *)calloc(1, malloc_header_size + size);
282
+ struct malloc_header *t = (struct malloc_header *)libc_calloc(1, malloc_header_size + size);
283
if (unlikely(!t)) fatal("mallocz() cannot allocate %zu bytes of memory (%zu with header).", size, malloc_header_size + size);
284
t->signature.magic = 0x0BADCAFE;
285
t->signature.trace = p;
@@ -184,7 +301,7 @@ char *strdupz_int(const char *s, const char *file, const char *function, size_t
301
size_t_atomic_count(add, p->allocations, 1);
302
size_t_atomic_bytes(add, p->bytes, size);
303
187
- struct malloc_header *t = (struct malloc_header *)malloc(malloc_header_size + size);
304
+ struct malloc_header *t = (struct malloc_header *)libc_malloc(malloc_header_size + size);
305
if (unlikely(!t)) fatal("strdupz() cannot allocate %zu bytes of memory (%zu with header).", size, malloc_header_size + size);
306
t->signature.magic = 0x0BADCAFE;
307
t->signature.trace = p;
@@ -216,7 +333,7 @@ void *reallocz_int(void *ptr, size_t size, const char *file, const char *functio
333
334
struct malloc_header *t = malloc_get_header(ptr, __FUNCTION__, file, function, line);
335
if(!t)
219
- return realloc(ptr, size);
336
+ return libc_realloc(ptr, size);
337
338
if(t->signature.size == size) return ptr;
339
size_t_atomic_count(add, t->signature.trace->free_calls, 1);
@@ -228,7 +345,7 @@ void *reallocz_int(void *ptr, size_t size, const char *file, const char *functio
345
size_t_atomic_count(add, p->allocations, 1);
346
size_t_atomic_bytes(add, p->bytes, size);
347
231
- t = (struct malloc_header *)realloc(t, malloc_header_size + size);
348
+ t = (struct malloc_header *)libc_realloc(t, malloc_header_size + size);
349
if (unlikely(!t)) fatal("reallocz() cannot allocate %zu bytes of memory (%zu with header).", size, malloc_header_size + size);
350
t->signature.magic = 0x0BADCAFE;
351
t->signature.trace = p;
@@ -242,12 +359,26 @@ void *reallocz_int(void *ptr, size_t size, const char *file, const char *functio
359
return (void *)&t->data;
360
}
361
362
+size_t mallocz_usable_size_int(void *ptr, const char *file, const char *function, size_t line) {
363
+ if(unlikely(!ptr)) return 0;
364
+
365
+ struct malloc_header *t = malloc_get_header(ptr, __FUNCTION__, file, function, line);
366
+ if(!t) {
367
+ if(libc_malloc_usable_size)
368
+ return libc_malloc_usable_size(ptr);
369
+ else
370
+ return 0;
371
+ }
372
+
373
+ return t->signature.size;
374
+}
375
+
376
void freez_int(void *ptr, const char *file, const char *function, size_t line) {
377
if(unlikely(!ptr)) return;
378
379
struct malloc_header *t = malloc_get_header(ptr, __FUNCTION__, file, function, line);
380
if(!t) {
250
- free(ptr);
381
+ libc_free(ptr);
382
return;
383
}
384
@@ -260,7 +391,7 @@ void freez_int(void *ptr, const char *file, const char *function, size_t line) {
391
memset(t, 0, malloc_header_size + t->signature.size);
392
#endif
393
263
- free(t);
394
+ libc_free(t);
395
}
396
#else
397
@@ -293,6 +424,10 @@ void *reallocz(void *ptr, size_t size) {
424
return p;
425
}
426
427
+void posix_memfree(void *ptr) {
428
+ free(ptr);
429
+}
430
+
431
#endif
432
433
// --------------------------------------------------------------------------------------------------------------------
libnetdata/libnetdata.h
+8
-3
@@ -15,9 +15,10 @@ extern "C" {
15
#define NETDATA_INTERNAL_CHECKS 1
16
#endif
17
18
-#if defined(NETDATA_INTERNAL_CHECKS) && !defined(NETDATA_TRACE_ALLOCATIONS)
19
-#define NETDATA_TRACE_ALLOCATIONS 1
20
-#endif
18
+// NETDATA_TRACE_ALLOCATIONS does not work under musl libc, so don't enable it
19
+//#if defined(NETDATA_INTERNAL_CHECKS) && !defined(NETDATA_TRACE_ALLOCATIONS)
20
+//#define NETDATA_TRACE_ALLOCATIONS 1
21
+//#endif
22
23
#define OS_LINUX 1
24
#define OS_FREEBSD 2
@@ -312,12 +313,14 @@ int malloc_trace_walkthrough(int (*callback)(void *item, void *data), void *data
313
#define mallocz(size) mallocz_int(size, __FILE__, __FUNCTION__, __LINE__)
314
#define reallocz(ptr, size) reallocz_int(ptr, size, __FILE__, __FUNCTION__, __LINE__)
315
#define freez(ptr) freez_int(ptr, __FILE__, __FUNCTION__, __LINE__)
316
+#define mallocz_usable_size(ptr) mallocz_usable_size_int(ptr, __FILE__, __FUNCTION__, __LINE__)
317
318
char *strdupz_int(const char *s, const char *file, const char *function, size_t line);
319
void *callocz_int(size_t nmemb, size_t size, const char *file, const char *function, size_t line);
320
void *mallocz_int(size_t size, const char *file, const char *function, size_t line);
321
void *reallocz_int(void *ptr, size_t size, const char *file, const char *function, size_t line);
322
void freez_int(void *ptr, const char *file, const char *function, size_t line);
323
+size_t mallocz_usable_size_int(void *ptr, const char *file, const char *function, size_t line);
324
325
#else // NETDATA_TRACE_ALLOCATIONS
326
char *strdupz(const char *s) MALLOCLIKE NEVERNULL;
@@ -327,6 +330,8 @@ void *reallocz(void *ptr, size_t size) MALLOCLIKE NEVERNULL;
330
void freez(void *ptr);
331
#endif // NETDATA_TRACE_ALLOCATIONS
332
333
+void posix_memfree(void *ptr);
334
+
335
void json_escape_string(char *dst, const char *src, size_t size);
336
void json_fix_string(char *s);
337
ml/ml.cc
+1
-1
@@ -111,7 +111,7 @@ char *ml_get_host_info(RRDHOST *RH) {
111
ConfigJson["enabled"] = false;
112
}
113
114
- return strdup(ConfigJson.dump(2, '\t').c_str());
114
+ return strdupz(ConfigJson.dump(2, '\t').c_str());
115
}
116
117
char *ml_get_host_runtime_info(RRDHOST *RH) {
web/api/web_api_v1.c
+1
-1
@@ -1325,7 +1325,7 @@ inline int web_client_api_request_v1_info_fill_buffer(RRDHOST *host, BUFFER *wb)
1325
buffer_strcat(wb, "\t\"ml-info\": ");
1326
buffer_strcat(wb, ml_info);
1327
1328
- free(ml_info);
1328
+ freez(ml_info);
1329
#endif
1330
1331
buffer_strcat(wb, "\n}");