@cryptotaxi247 / netdata-1 / commits / 070fcf1dc

Detect memory leaks (#19811)

* make stderr remain intact when FSANITIZE_ADDRESS is set * switching users makes the sanitizer fail * check for failure to memory map journal file * fix memory leak in buildinfo * fix memory leak in stream_sender_structures_init(), when it is called via rrdhost_update() * log more information when running under FSANITIZE_ADDRESS * fix formatting

Costa Tsaousis committed Mar 9, 2025 at 11:22 UTC 070fcf1dc940baef5e31db835e3096d53e3bcb8c
10 files changed +99 -59
src/daemon/buildinfo.c
+44 -41
@@ -159,6 +159,7 @@ static struct {
159 const char *json;
160 bool status;
161 const char *value;
162 + bool value_allocated;
163 } BUILD_INFO[] = {
164 [BIB_PACKAGING_NETDATA_VERSION] = {
165 .category = BIC_PACKAGING,
@@ -1117,7 +1118,14 @@ static struct {
1118 };
1119
1120 static void build_info_set_value(BUILD_INFO_SLOT slot, const char *value) {
1121 + const char *old = BUILD_INFO[slot].value;
1122 +
1123 BUILD_INFO[slot].value = value;
1124 +
1125 + if(BUILD_INFO[slot].value_allocated)
1126 + freez((void *)old);
1127 +
1128 + BUILD_INFO[slot].value_allocated = false;
1129 }
1130
1131 static void build_info_append_value(BUILD_INFO_SLOT slot, const char *value) {
@@ -1133,13 +1141,30 @@ static void build_info_append_value(BUILD_INFO_SLOT slot, const char *value) {
1141 else
1142 strcpy(buf, value);
1143
1136 - freez((void *)BUILD_INFO[slot].value);
1144 + const char *old = BUILD_INFO[slot].value;
1145 +
1146 BUILD_INFO[slot].value = strdupz(buf);
1147 +
1148 + if(BUILD_INFO[slot].value_allocated)
1149 + freez((void *)old);
1150 +
1151 + BUILD_INFO[slot].value_allocated = true;
1152 }
1153
1154 static void build_info_set_value_strdupz(BUILD_INFO_SLOT slot, const char *value) {
1155 if(!value) value = "";
1142 - build_info_set_value(slot, strdupz(value));
1156 +
1157 + if(BUILD_INFO[slot].value && strcmp(BUILD_INFO[slot].value, value) == 0)
1158 + return;
1159 +
1160 + const char *old = BUILD_INFO[slot].value;
1161 +
1162 + BUILD_INFO[slot].value = strdupz(value);
1163 +
1164 + if(old && BUILD_INFO[slot].value_allocated)
1165 + freez((void *)old);
1166 +
1167 + BUILD_INFO[slot].value_allocated = true;
1168 }
1169
1170 static void build_info_set_status(BUILD_INFO_SLOT slot, bool status) {
@@ -1375,18 +1400,7 @@ __attribute__((constructor)) void initialize_build_info(void) {
1400 // system info
1401
1402 static void populate_system_info(void) {
1378 - static bool populated = false;
1379 - static SPINLOCK spinlock = SPINLOCK_INITIALIZER;
1380 -
1381 - if(populated)
1382 - return;
1383 -
1384 - spinlock_lock(&spinlock);
1385 -
1386 - if(populated) {
1387 - spinlock_unlock(&spinlock);
1388 - return;
1389 - }
1403 + FUNCTION_RUN_ONCE();
1404
1405 struct rrdhost_system_info *system_info;
1406 bool free_system_info = false;
@@ -1441,9 +1455,6 @@ static void populate_system_info(void) {
1455
1456 if(free_system_info)
1457 rrdhost_system_info_free(system_info);
1444 -
1445 - populated = true;
1446 - spinlock_unlock(&spinlock);
1458 }
1459
1460 // ----------------------------------------------------------------------------
@@ -1496,43 +1507,35 @@ void get_install_type(struct rrdhost_system_info *system_info) {
1507 }
1508
1509 static struct {
1499 - SPINLOCK spinlock;
1500 - bool populated;
1510 char *install_type;
1511 char *prebuilt_arch;
1512 char *prebuilt_distro;
1513 } BUILD_PACKAGING_INFO = { 0 };
1514
1515 static void populate_packaging_info() {
1507 - if(!BUILD_PACKAGING_INFO.populated) {
1508 - spinlock_lock(&BUILD_PACKAGING_INFO.spinlock);
1509 - if(!BUILD_PACKAGING_INFO.populated) {
1510 - BUILD_PACKAGING_INFO.populated = true;
1516 + FUNCTION_RUN_ONCE();
1517
1512 - get_install_type_internal(&BUILD_PACKAGING_INFO.install_type, &BUILD_PACKAGING_INFO.prebuilt_arch, &BUILD_PACKAGING_INFO.prebuilt_distro);
1518 + get_install_type_internal(&BUILD_PACKAGING_INFO.install_type, &BUILD_PACKAGING_INFO.prebuilt_arch, &BUILD_PACKAGING_INFO.prebuilt_distro);
1519
1514 - if(!BUILD_PACKAGING_INFO.install_type)
1515 - BUILD_PACKAGING_INFO.install_type = "unknown";
1520 + if(!BUILD_PACKAGING_INFO.install_type)
1521 + BUILD_PACKAGING_INFO.install_type = "unknown";
1522
1517 - if(!BUILD_PACKAGING_INFO.prebuilt_arch)
1518 - BUILD_PACKAGING_INFO.prebuilt_arch = "unknown";
1523 + if(!BUILD_PACKAGING_INFO.prebuilt_arch)
1524 + BUILD_PACKAGING_INFO.prebuilt_arch = "unknown";
1525
1520 - if(!BUILD_PACKAGING_INFO.prebuilt_distro)
1521 - BUILD_PACKAGING_INFO.prebuilt_distro = "unknown";
1526 + if(!BUILD_PACKAGING_INFO.prebuilt_distro)
1527 + BUILD_PACKAGING_INFO.prebuilt_distro = "unknown";
1528
1523 - build_info_set_value(BIB_PACKAGING_INSTALL_TYPE, strdupz(BUILD_PACKAGING_INFO.install_type));
1524 - build_info_set_value(BIB_PACKAGING_ARCHITECTURE, strdupz(BUILD_PACKAGING_INFO.prebuilt_arch));
1525 - build_info_set_value(BIB_PACKAGING_DISTRO, strdupz(BUILD_PACKAGING_INFO.prebuilt_distro));
1529 + build_info_set_value_strdupz(BIB_PACKAGING_INSTALL_TYPE, BUILD_PACKAGING_INFO.install_type);
1530 + build_info_set_value_strdupz(BIB_PACKAGING_ARCHITECTURE, BUILD_PACKAGING_INFO.prebuilt_arch);
1531 + build_info_set_value_strdupz(BIB_PACKAGING_DISTRO, BUILD_PACKAGING_INFO.prebuilt_distro);
1532
1527 - CLEAN_BUFFER *wb = buffer_create(0, NULL);
1528 - ND_PROFILE_2buffer(wb, nd_profile_detect_and_configure(false), " ");
1529 - build_info_set_value_strdupz(BIB_RUNTIME_PROFILE, buffer_tostring(wb));
1533 + CLEAN_BUFFER *wb = buffer_create(0, NULL);
1534 + ND_PROFILE_2buffer(wb, nd_profile_detect_and_configure(false), " ");
1535 + build_info_set_value_strdupz(BIB_RUNTIME_PROFILE, buffer_tostring(wb));
1536
1531 - build_info_set_status(BIB_RUNTIME_PARENT, stream_conf_is_parent(false));
1532 - build_info_set_status(BIB_RUNTIME_CHILD, stream_conf_is_child());
1533 - }
1534 - spinlock_unlock(&BUILD_PACKAGING_INFO.spinlock);
1535 - }
1537 + build_info_set_status(BIB_RUNTIME_PARENT, stream_conf_is_parent(false));
1538 + build_info_set_status(BIB_RUNTIME_CHILD, stream_conf_is_child());
1539
1540 OS_SYSTEM_MEMORY sm = os_system_memory(true);
1541 if(OS_SYSTEM_MEMORY_OK(sm)) {
src/daemon/daemon.c
+6
@@ -115,6 +115,7 @@ static int become_user(const char *username, int pid_fd) {
115 if(supplementary_groups)
116 freez(supplementary_groups);
117
118 +#if !defined(FSANITIZE_ADDRESS)
119 if(os_setresgid(gid, gid, gid) != 0) {
120 netdata_log_error("Cannot switch to user's %s group (gid: %u).", username, gid);
121 return -1;
@@ -129,10 +130,12 @@ static int become_user(const char *username, int pid_fd) {
130 netdata_log_error("Cannot switch to user's %s group (gid: %u).", username, gid);
131 return -1;
132 }
133 +
134 if(setegid(gid) != 0) {
135 netdata_log_error("Cannot effectively switch to user's %s group (gid: %u).", username, gid);
136 return -1;
137 }
138 +
139 if(setuid(uid) != 0) {
140 netdata_log_error("Cannot switch to user %s (uid: %u).", username, uid);
141 return -1;
@@ -141,6 +144,9 @@ static int become_user(const char *username, int pid_fd) {
144 netdata_log_error("Cannot effectively switch to user %s (uid: %u).", username, uid);
145 return -1;
146 }
147 +#else
148 + fprintf(stderr, "Running with a Sanitizer, skipping setuid/setgid\n");
149 +#endif
150
151 return(0);
152 }
src/daemon/main.c
+10
@@ -742,11 +742,16 @@ int netdata_main(int argc, char **argv) {
742 }
743 }
744
745 +#if !defined(FSANITIZE_ADDRESS)
746 if (close_open_fds == true) {
747 // close all open file descriptors, except the standard ones
748 // the caller may have left open files (lxc-attach has this issue)
749 os_close_all_non_std_open_fds_except(NULL, 0, 0);
750 }
751 +#else
752 + fprintf(stderr, "Running with a Sanitizer, custom allocators are disabled.\n");
753 + fprintf(stderr, "Running with a Sanitizer, not closing open fds.\n");
754 +#endif
755
756 if(!config_loaded) {
757 netdata_conf_load(NULL, 0, &user);
@@ -1141,6 +1146,11 @@ int main(int argc, char *argv[])
1146 if (rc != 10)
1147 return rc;
1148
1149 +#if defined(FSANITIZE_ADDRESS)
1150 + fprintf(stdout, "STDOUT: Sanitizers mode enabled...\n");
1151 + fprintf(stderr, "STDERR: Sanitizers mode enabled...\n");
1152 +#endif
1153 +
1154 nd_process_signals();
1155 return 1;
1156 }
src/daemon/signal-handler.c
-9
@@ -87,9 +87,6 @@ static void posix_unmask_my_signals(void) {
87 }
88
89 void nd_initialize_signals(void) {
90 -#if defined(FSANITIZE_ADDRESS)
91 - ;
92 -#else
90 signals_block_all_except_deadly();
91
92 // Catch signals which we want to use
@@ -112,7 +109,6 @@ void nd_initialize_signals(void) {
109 if(sigaction(signals_waiting[i].signo, &sa, NULL) == -1)
110 netdata_log_error("SIGNAL: Failed to change signal handler for: %s", signals_waiting[i].name);
111 }
115 -#endif
112 }
113
114 static void process_triggered_signals(void) {
@@ -165,10 +161,6 @@ static void process_triggered_signals(void) {
161 }
162
163 void nd_process_signals(void) {
168 -#if defined(FSANITIZE_ADDRESS)
169 - while(true)
170 - pause();
171 -#else
164 posix_unmask_my_signals();
165 const usec_t save_every_ut = 15 * 60 * USEC_PER_SEC;
166 usec_t last_update_mt = now_monotonic_usec();
@@ -183,5 +175,4 @@ void nd_process_signals(void) {
175 poll(NULL, 0, 13 * MSEC_PER_SEC + 379);
176 process_triggered_signals();
177 }
186 -#endif
178 }
src/database/engine/journalfile.c
+3 -2
@@ -1328,7 +1328,8 @@ void journalfile_migrate_to_v2_callback(Word_t section, unsigned datafile_fileno
1328
1329 int fd_v2;
1330 uint8_t *data_start = nd_mmap_advanced(path, total_file_size, MAP_SHARED, 0, false, true, &fd_v2);
1331 - uint8_t *data = data_start;
1331 + if(!data_start)
1332 + fatal("DBENGINE: failed to memory map file '%s' of size %zu.", path, total_file_size);
1333
1334 memset(data_start, 0, extent_offset);
1335
@@ -1353,7 +1354,7 @@ void journalfile_migrate_to_v2_callback(Word_t section, unsigned datafile_fileno
1354
1355 struct journal_v2_block_trailer *journal_v2_trailer;
1356
1356 - data = journalfile_v2_write_extent_list(JudyL_extents_pos, data_start + extent_offset);
1357 + uint8_t *data = journalfile_v2_write_extent_list(JudyL_extents_pos, data_start + extent_offset);
1358 internal_error(true, "DBENGINE: write extent list so far %llu", (now_monotonic_usec() - start_loading) / USEC_PER_MS);
1359
1360 fatal_assert(data == data_start + extent_offset_trailer);
src/database/rrdhost.c
+1 -2
@@ -525,8 +525,7 @@ static void rrdhost_update(RRDHOST *host
525 , const char *prog_version
526 , int update_every
527 , long history
528 - ,
529 - RRD_DB_MODE mode
528 + , RRD_DB_MODE mode
529 , bool health
530 , bool stream
531 , STRING *parents
src/libnetdata/log/nd_log-internals.c
+8
@@ -344,7 +344,11 @@ struct nd_log nd_log = {
344 .method = NDLM_DEFAULT,
345 .format = NDLF_LOGFMT,
346 .filename = LOG_DIR "/collector.log",
347 +#if defined(FSANITIZE_ADDRESS)
348 + .fd = -1,
349 +#else
350 .fd = STDERR_FILENO,
351 +#endif
352 .fp = NULL,
353 .min_priority = NDLP_INFO,
354 .limits = ND_LOG_LIMITS_DEFAULT,
@@ -354,7 +358,11 @@ struct nd_log nd_log = {
358 .method = NDLM_DISABLED,
359 .format = NDLF_LOGFMT,
360 .filename = LOG_DIR "/debug.log",
361 +#if defined(FSANITIZE_ADDRESS)
362 + .fd = -1,
363 +#else
364 .fd = STDOUT_FILENO,
365 +#endif
366 .fp = NULL,
367 .min_priority = NDLP_DEBUG,
368 .limits = ND_LOG_LIMITS_UNLIMITED,
src/libnetdata/log/nd_log.h
+3 -1
@@ -15,7 +15,7 @@ extern "C" {
15 #define ND_LOG_DEFAULT_THROTTLE_PERIOD 60
16
17 void errno_clear(void);
18 -int nd_log_systemd_journal_fd(void);
18 +
19 void nd_log_set_user_settings(ND_LOG_SOURCES source, const char *setting);
20 void nd_log_set_facility(const char *facility);
21 void nd_log_set_priority_level(const char *setting);
@@ -44,8 +44,10 @@ void nd_log_register_fatal_data_cb(log_event_t cb);
44 typedef void (*fatal_event_t)(void);
45 void nd_log_register_fatal_final_cb(fatal_event_t cb);
46
47 +int nd_log_systemd_journal_fd(void);
48 int nd_log_health_fd(void);
49 int nd_log_collectors_fd(void);
50 +
51 typedef bool (*log_formatter_callback_t)(BUFFER *wb, void *data);
52
53 struct log_stack_entry {
src/libnetdata/spawn_server/spawn_server_nofork.c
+4
@@ -1081,6 +1081,10 @@ SPAWN_SERVER* spawn_server_create(SPAWN_SERVER_OPTIONS options, const char *name
1081 os_setproctitle(buf, server->argc, server->argv);
1082
1083 replace_stdio_with_dev_null();
1084 +
1085 + if(nd_log_collectors_fd() != STDERR_FILENO)
1086 + dup2(nd_log_collectors_fd(), STDERR_FILENO);
1087 +
1088 int fds_to_keep[] = {
1089 server->sock,
1090 server->pipe[1],
src/streaming/stream-sender-api.c
+20 -4
@@ -46,12 +46,28 @@ void stream_sender_structures_init(RRDHOST *host, bool stream, STRING *parents,
46 spinlock_init(&host->sender->spinlock);
47 replication_sender_init(host->sender);
48
49 - host->stream.snd.destination = string_dup(parents);
49 + // gracefully swap destination
50 + if(host->stream.snd.destination != parents) {
51 + STRING *t = string_dup(parents);
52 + SWAP(host->stream.snd.destination, t);
53 + string_freez(t);
54 + }
55 rrdhost_stream_parents_update_from_destination(host);
56
52 - host->stream.snd.api_key = string_dup(api_key);
53 - host->stream.snd.charts_matching = simple_pattern_create(
54 - string2str(send_charts_matching), NULL, SIMPLE_PATTERN_EXACT, true);
57 + // gracefully swap api_key
58 + if(host->stream.snd.api_key != api_key) {
59 + STRING *t = string_dup(api_key);
60 + SWAP(host->stream.snd.api_key, t);
61 + string_freez(t);
62 + }
63 +
64 + // gracefully swap send_charts_matching
65 + {
66 + SIMPLE_PATTERN *t = simple_pattern_create(
67 + string2str(send_charts_matching), NULL, SIMPLE_PATTERN_EXACT, true);
68 + SWAP(host->stream.snd.charts_matching, t);
69 + simple_pattern_free(t);
70 + }
71
72 rrdhost_option_set(host, RRDHOST_OPTION_SENDER_ENABLED);
73 }