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
}