@cryptotaxi247 / netdata-1 / commits / 2974f525e

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}");