@cryptotaxi247 / netdata-1 / commits / 42097e1d9

Fix memory-safety and correctness bugs surfaced by Coverity audit (part 5) (#22279)

* dictionary: retry view inserts after stale entry cleanup Coverity CID 414657 (REVERSE_INULL): a rejected item-acquire path in `dict_item_add_or_reset_value_and_acquire()` left `item` non-NULL, so the `do/while` loop exited instead of retrying after stale view-entry cleanup. Clear `item` before retrying and cover the stale-view replacement path in the existing dictionary unittest. * dictionary: release view item on stale replacement branch in unittest Release the acquired `view_item2` on the stale/deleted failure branch of the view-replacement regression test, matching the release on the success branch so the test does not leak an acquired reference. * ebpf.plugin: make module state accesses atomic Coverity CID 405093 (MISSING_LOCK): ebpf_module.enabled was sampled from stats and shutdown paths while module exit code updated the same plain enum under a different synchronization regime. Convert the live cross-thread reads and writes to atomic helpers so those state checks stay defined without changing the existing lock layout. * ebpf: start function threads outside cleanup lock Coverity CID 405089 (SLEEP): Function-triggered eBPF socket restarts held ebpf_exit_cleanup while nd_thread_create could wait and retry. Gate the new thread until state is published, so creation happens outside the cleanup mutex without racing shutdown. * systemd-journal: pass depth+1 to recursive directory scan nd_journal_directory_scan_recursively() used depth++ (post-increment) when recursing into subdirectories, which passes the caller's current depth to the recursive call and then increments the caller's local counter across sibling iterations. Effect: the 2nd, 3rd, ... sibling subdirectories of the same parent get inflated depths and prematurely hit VAR_LOG_JOURNAL_MAX_DEPTH, silently truncating legitimate scans. Use depth + 1 instead so every recursion starts exactly one level deeper than the current frame. * systemd-journal: log visited directory tracking failures When dictionary_set() returns NULL the recursive scan still closes the current directory and bails out, but an operator looking at truncated journal discovery had no way to know why. Log the failure before returning so the condition is diagnosable. --------- Co-authored-by: Costa Tsaousis <costa@netdata.cloud>

Stelios Fragkakis committed Apr 25, 2026 at 20:23 UTC 42097e1d9512eeb6ad0dc5ee5277aba50d83e866
24 files changed +196 -80
src/collectors/ebpf.plugin/ebpf.c
+19 -12
@@ -1001,7 +1001,7 @@ static inline void ebpf_check_before2go()
1001 int active_count = 0;
1002 netdata_mutex_lock(&ebpf_exit_cleanup);
1003 for (j = 0; ebpf_modules[j].info.thread_name != NULL; j++) {
1004 - if (ebpf_modules[j].enabled < NETDATA_THREAD_EBPF_STOPPING)
1004 + if (ebpf_module_enabled_get(&ebpf_modules[j]) < NETDATA_THREAD_EBPF_STOPPING)
1005 active_count++;
1006 }
1007 netdata_mutex_unlock(&ebpf_exit_cleanup);
@@ -1124,7 +1124,9 @@ void ebpf_stop_threads(int sig)
1124
1125 int i;
1126 for (i = 0; ebpf_modules[i].info.thread_name != NULL; i++) {
1127 - if (ebpf_modules[i].enabled < NETDATA_THREAD_EBPF_STOPPING && ebpf_modules[i].thread &&
1127 + enum ebpf_threads_status enabled = ebpf_module_enabled_get(&ebpf_modules[i]);
1128 +
1129 + if (enabled < NETDATA_THREAD_EBPF_STOPPING && ebpf_modules[i].thread &&
1130 ebpf_modules[i].thread->thread) {
1131 nd_thread_signal_cancel(ebpf_modules[i].thread->thread);
1132 #ifdef NETDATA_DEV_MODE
@@ -1135,12 +1137,14 @@ void ebpf_stop_threads(int sig)
1137 netdata_mutex_unlock(&ebpf_exit_cleanup);
1138
1139 for (i = 0; ebpf_modules[i].info.thread_name != NULL; i++) {
1138 - if (ebpf_modules[i].enabled < NETDATA_THREAD_EBPF_STOPPED && ebpf_threads[i].thread) {
1140 + enum ebpf_threads_status enabled = ebpf_module_enabled_get(&ebpf_modules[i]);
1141 +
1142 + if (enabled < NETDATA_THREAD_EBPF_STOPPED && ebpf_threads[i].thread) {
1143 netdata_log_info(
1144 "EBPF SHUTDOWN: about to join module[%d]='%s' (state=%u).",
1145 i,
1146 ebpf_modules[i].info.thread_name,
1143 - ebpf_modules[i].enabled);
1147 + enabled);
1148 usec_t join_started_ut = now_monotonic_usec();
1149 nd_thread_join(ebpf_threads[i].thread);
1150 usec_t join_duration_ut = now_monotonic_usec() - join_started_ut;
@@ -1576,7 +1580,7 @@ static inline void ebpf_send_hash_table_pid_data(char *chart, uint32_t idx)
1580 if (wem->functions.apps_routine)
1581 write_chart_dimension(
1582 (char *)wem->info.thread_name,
1579 - (wem->enabled < NETDATA_THREAD_EBPF_STOPPING) ? wem->hash_table_stats[idx] : 0);
1583 + (ebpf_module_enabled_get(wem) < NETDATA_THREAD_EBPF_STOPPING) ? wem->hash_table_stats[idx] : 0);
1584 }
1585 ebpf_write_end_chart();
1586 }
@@ -1594,7 +1598,8 @@ static inline void ebpf_send_global_hash_table_data()
1598 for (i = 0; i < EBPF_MODULE_FUNCTION_IDX; i++) {
1599 ebpf_module_t *wem = &ebpf_modules[i];
1600 write_chart_dimension(
1597 - (char *)wem->info.thread_name, (wem->enabled < NETDATA_THREAD_EBPF_STOPPING) ? NETDATA_CONTROLLER_END : 0);
1601 + (char *)wem->info.thread_name,
1602 + (ebpf_module_enabled_get(wem) < NETDATA_THREAD_EBPF_STOPPING) ? NETDATA_CONTROLLER_END : 0);
1603 }
1604 ebpf_write_end_chart();
1605 }
@@ -1616,7 +1621,8 @@ void ebpf_send_statistic_data()
1621 if (wem->functions.fnct_routine)
1622 continue;
1623
1619 - write_chart_dimension((char *)wem->info.thread_name, (wem->enabled < NETDATA_THREAD_EBPF_STOPPING) ? 1 : 0);
1624 + write_chart_dimension(
1625 + (char *)wem->info.thread_name, (ebpf_module_enabled_get(wem) < NETDATA_THREAD_EBPF_STOPPING) ? 1 : 0);
1626 }
1627 ebpf_write_end_chart();
1628
@@ -1635,7 +1641,7 @@ void ebpf_send_statistic_data()
1641
1642 write_chart_dimension(
1643 (char *)wem->info.thread_name,
1638 - (wem->lifetime && wem->enabled < NETDATA_THREAD_EBPF_STOPPING) ?
1644 + (wem->lifetime && ebpf_module_enabled_get(wem) < NETDATA_THREAD_EBPF_STOPPING) ?
1645 (long long)(wem->lifetime - wem->running_time) :
1646 0);
1647 }
@@ -1682,13 +1688,14 @@ void ebpf_send_statistic_data()
1688 continue;
1689
1690 ebpf_write_begin_chart(NETDATA_MONITORING_FAMILY, wem->functions.fcnt_thread_chart_name, "");
1685 - write_chart_dimension((char *)wem->info.thread_name, (wem->enabled < NETDATA_THREAD_EBPF_STOPPING) ? 1 : 0);
1691 + write_chart_dimension(
1692 + (char *)wem->info.thread_name, (ebpf_module_enabled_get(wem) < NETDATA_THREAD_EBPF_STOPPING) ? 1 : 0);
1693 ebpf_write_end_chart();
1694
1695 ebpf_write_begin_chart(NETDATA_MONITORING_FAMILY, wem->functions.fcnt_thread_lifetime_name, "");
1696 write_chart_dimension(
1697 (char *)wem->info.thread_name,
1691 - (wem->lifetime && wem->enabled < NETDATA_THREAD_EBPF_STOPPING) ?
1698 + (wem->lifetime && ebpf_module_enabled_get(wem) < NETDATA_THREAD_EBPF_STOPPING) ?
1699 (long long)(wem->lifetime - wem->running_time) :
1700 0);
1701 ebpf_write_end_chart();
@@ -2372,8 +2379,8 @@ int main(int argc, char **argv)
2379 ebpf_module_t *em = &ebpf_modules[i];
2380 em->thread = st;
2381 em->thread_id = i;
2375 - if (em->enabled != NETDATA_THREAD_EBPF_NOT_RUNNING) {
2376 - em->enabled = NETDATA_THREAD_EBPF_RUNNING;
2382 + if (ebpf_module_enabled_get(em) != NETDATA_THREAD_EBPF_NOT_RUNNING) {
2383 + ebpf_module_enabled_set(em, NETDATA_THREAD_EBPF_RUNNING);
2384 em->lifetime = EBPF_NON_FUNCTION_LIFE_TIME;
2385
2386 if (em->functions.apps_routine && (em->apps_charts || em->cgroup_charts)) {
src/collectors/ebpf.plugin/ebpf.h
+16 -2
@@ -367,12 +367,26 @@ static inline bool ebpf_plugin_stop(void)
367 nd_thread_signaled_to_cancel();
368 }
369
370 +// `enabled` is sampled from stats/shutdown paths without a single shared mutex.
371 +// Keep those state transitions defined without changing the plugin's lock layout.
372 +static inline enum ebpf_threads_status ebpf_module_enabled_get(ebpf_module_t *em)
373 +{
374 + return __atomic_load_n(&em->enabled, __ATOMIC_RELAXED);
375 +}
376 +
377 +static inline void ebpf_module_enabled_set(ebpf_module_t *em, enum ebpf_threads_status enabled)
378 +{
379 + __atomic_store_n(&em->enabled, enabled, __ATOMIC_RELAXED);
380 +}
381 +
382 static inline bool ebpf_module_thread_has_valid_state(ebpf_module_t *em)
383 {
372 - if (likely(em->enabled == NETDATA_THREAD_EBPF_RUNNING || em->enabled == NETDATA_THREAD_EBPF_FUNCTION_RUNNING))
384 + enum ebpf_threads_status enabled = ebpf_module_enabled_get(em);
385 +
386 + if (likely(enabled == NETDATA_THREAD_EBPF_RUNNING || enabled == NETDATA_THREAD_EBPF_FUNCTION_RUNNING))
387 return true;
388
375 - collector_error("Cannot start thread %s with invalid state %u.", em->info.thread_name, (unsigned int)em->enabled);
389 + collector_error("Cannot start thread %s with invalid state %u.", em->info.thread_name, (unsigned int)enabled);
390 return false;
391 }
392
src/collectors/ebpf.plugin/ebpf_cachestat.c
+3 -3
@@ -590,7 +590,7 @@ static void ebpf_cachestat_exit(void *pptr)
590
591 if (!cachestat_safe_clean) {
592 netdata_mutex_lock(&ebpf_exit_cleanup);
593 - em->enabled = NETDATA_THREAD_EBPF_STOPPED;
593 + ebpf_module_enabled_set(em, NETDATA_THREAD_EBPF_STOPPED);
594 netdata_mutex_unlock(&ebpf_exit_cleanup);
595 return;
596 }
@@ -609,7 +609,7 @@ static void ebpf_cachestat_exit(void *pptr)
609 sem_post(shm_mutex_ebpf_integration);
610 }
611
612 - if (em->enabled == NETDATA_THREAD_EBPF_FUNCTION_RUNNING && !ebpf_plugin_stop()) {
612 + if (ebpf_module_enabled_get(em) == NETDATA_THREAD_EBPF_FUNCTION_RUNNING && !ebpf_plugin_stop()) {
613 netdata_mutex_lock(&lock);
614 if (em->cgroup_charts) {
615 ebpf_obsolete_cachestat_cgroup_charts(em);
@@ -630,7 +630,7 @@ static void ebpf_cachestat_exit(void *pptr)
630 em->functions.bpf_unload(em);
631
632 netdata_mutex_lock(&ebpf_exit_cleanup);
633 - em->enabled = NETDATA_THREAD_EBPF_STOPPED;
633 + ebpf_module_enabled_set(em, NETDATA_THREAD_EBPF_STOPPED);
634 netdata_mutex_unlock(&ebpf_exit_cleanup);
635
636 freez(cachestat_vector);
src/collectors/ebpf.plugin/ebpf_dcstat.c
+2 -2
@@ -505,7 +505,7 @@ static void ebpf_dcstat_exit(void *pptr)
505 sem_post(shm_mutex_ebpf_integration);
506 }
507
508 - if (em->enabled == NETDATA_THREAD_EBPF_FUNCTION_RUNNING && !ebpf_plugin_stop()) {
508 + if (ebpf_module_enabled_get(em) == NETDATA_THREAD_EBPF_FUNCTION_RUNNING && !ebpf_plugin_stop()) {
509 netdata_mutex_lock(&lock);
510 if (em->cgroup_charts) {
511 ebpf_obsolete_dc_cgroup_charts(em);
@@ -526,7 +526,7 @@ static void ebpf_dcstat_exit(void *pptr)
526 em->functions.bpf_unload(em);
527
528 netdata_mutex_lock(&ebpf_exit_cleanup);
529 - em->enabled = NETDATA_THREAD_EBPF_STOPPED;
529 + ebpf_module_enabled_set(em, NETDATA_THREAD_EBPF_STOPPED);
530 netdata_mutex_unlock(&ebpf_exit_cleanup);
531 }
532
src/collectors/ebpf.plugin/ebpf_disk.c
+3 -3
@@ -478,12 +478,12 @@ static void ebpf_disk_exit(void *pptr)
478
479 if (!disk_safe_clean) {
480 netdata_mutex_lock(&ebpf_exit_cleanup);
481 - em->enabled = NETDATA_THREAD_EBPF_STOPPED;
481 + ebpf_module_enabled_set(em, NETDATA_THREAD_EBPF_STOPPED);
482 netdata_mutex_unlock(&ebpf_exit_cleanup);
483 return;
484 }
485
486 - if (em->enabled == NETDATA_THREAD_EBPF_FUNCTION_RUNNING && !ebpf_plugin_stop()) {
486 + if (ebpf_module_enabled_get(em) == NETDATA_THREAD_EBPF_FUNCTION_RUNNING && !ebpf_plugin_stop()) {
487 netdata_mutex_lock(&lock);
488 ebpf_obsolete_disk_global(em);
489 netdata_mutex_unlock(&lock);
@@ -510,7 +510,7 @@ static void ebpf_disk_exit(void *pptr)
510 em->functions.bpf_unload(em);
511
512 netdata_mutex_lock(&ebpf_exit_cleanup);
513 - em->enabled = NETDATA_THREAD_EBPF_STOPPED;
513 + ebpf_module_enabled_set(em, NETDATA_THREAD_EBPF_STOPPED);
514 netdata_mutex_unlock(&ebpf_exit_cleanup);
515 }
516
src/collectors/ebpf.plugin/ebpf_fd.c
+3 -3
@@ -621,7 +621,7 @@ static void ebpf_fd_exit(void *pptr)
621
622 if (!fd_safe_clean) {
623 netdata_mutex_lock(&ebpf_exit_cleanup);
624 - em->enabled = NETDATA_THREAD_EBPF_STOPPED;
624 + ebpf_module_enabled_set(em, NETDATA_THREAD_EBPF_STOPPED);
625 netdata_mutex_unlock(&ebpf_exit_cleanup);
626 return;
627 }
@@ -642,7 +642,7 @@ static void ebpf_fd_exit(void *pptr)
642 sem_post(shm_mutex_ebpf_integration);
643 }
644
645 - if (em->enabled == NETDATA_THREAD_EBPF_FUNCTION_RUNNING && !ebpf_plugin_stop()) {
645 + if (ebpf_module_enabled_get(em) == NETDATA_THREAD_EBPF_FUNCTION_RUNNING && !ebpf_plugin_stop()) {
646 netdata_mutex_lock(&lock);
647 if (em->cgroup_charts) {
648 ebpf_obsolete_fd_cgroup_charts(em);
@@ -668,7 +668,7 @@ static void ebpf_fd_exit(void *pptr)
668 em->functions.bpf_unload(em);
669
670 netdata_mutex_lock(&ebpf_exit_cleanup);
671 - em->enabled = NETDATA_THREAD_EBPF_STOPPED;
671 + ebpf_module_enabled_set(em, NETDATA_THREAD_EBPF_STOPPED);
672 netdata_mutex_unlock(&ebpf_exit_cleanup);
673 }
674
src/collectors/ebpf.plugin/ebpf_filesystem.c
+3 -3
@@ -905,12 +905,12 @@ static void ebpf_filesystem_exit(void *pptr)
905 }
906 if (!dimensions && !filesystem_hash_values && !has_resources) {
907 netdata_mutex_lock(&ebpf_exit_cleanup);
908 - em->enabled = NETDATA_THREAD_EBPF_STOPPED;
908 + ebpf_module_enabled_set(em, NETDATA_THREAD_EBPF_STOPPED);
909 netdata_mutex_unlock(&ebpf_exit_cleanup);
910 return;
911 }
912
913 - if (em->enabled == NETDATA_THREAD_EBPF_FUNCTION_RUNNING && !ebpf_plugin_stop()) {
913 + if (ebpf_module_enabled_get(em) == NETDATA_THREAD_EBPF_FUNCTION_RUNNING && !ebpf_plugin_stop()) {
914 netdata_mutex_lock(&lock);
915 ebpf_obsolete_filesystem_global(em);
916
@@ -930,7 +930,7 @@ static void ebpf_filesystem_exit(void *pptr)
930 em->functions.bpf_unload(em);
931
932 netdata_mutex_lock(&ebpf_exit_cleanup);
933 - em->enabled = NETDATA_THREAD_EBPF_STOPPED;
933 + ebpf_module_enabled_set(em, NETDATA_THREAD_EBPF_STOPPED);
934 netdata_mutex_unlock(&ebpf_exit_cleanup);
935 }
936
src/collectors/ebpf.plugin/ebpf_functions.c
+59 -12
@@ -8,6 +8,30 @@
8 * EBPF FUNCTION COMMON
9 *****************************************************************/
10
11 +typedef struct ebpf_function_thread_start {
12 + ebpf_module_t *em;
13 + void (*start_routine)(void *);
14 + bool ready;
15 + bool run;
16 +} ebpf_function_thread_start_t;
17 +
18 +static void ebpf_function_thread_start(void *ptr)
19 +{
20 + ebpf_function_thread_start_t *ctx = ptr;
21 +
22 + // Keep the new thread parked until its owner publishes the module state.
23 + while (!__atomic_load_n(&ctx->ready, __ATOMIC_ACQUIRE))
24 + tinysleep();
25 +
26 + bool run = __atomic_load_n(&ctx->run, __ATOMIC_ACQUIRE);
27 + ebpf_module_t *em = ctx->em;
28 + void (*start_routine)(void *) = ctx->start_routine;
29 + freez(ctx);
30 +
31 + if (run)
32 + start_routine(em);
33 +}
34 +
35 /**
36 * Function Start thread
37 *
@@ -24,16 +48,40 @@ static int ebpf_function_start_thread(ebpf_module_t *em, int period)
48 if (period <= 0)
49 period = EBPF_DEFAULT_LIFETIME;
50
27 - st->thread = NULL;
28 - em->enabled = NETDATA_THREAD_EBPF_FUNCTION_RUNNING;
29 - em->lifetime = period;
30 -
51 #ifdef NETDATA_INTERNAL_CHECKS
52 netdata_log_info("Starting thread %s with lifetime = %d", em->info.thread_name, period);
53 #endif
54
35 - st->thread = nd_thread_create(st->name, NETDATA_THREAD_OPTION_DEFAULT, st->start_routine, em);
36 - return st->thread ? 0 : 1;
55 + ebpf_function_thread_start_t *ctx = callocz(1, sizeof(*ctx));
56 + ctx->em = em;
57 + ctx->start_routine = st->start_routine;
58 +
59 + ND_THREAD *thread = nd_thread_create(st->name, NETDATA_THREAD_OPTION_DEFAULT, ebpf_function_thread_start, ctx);
60 + if (!thread) {
61 + freez(ctx);
62 + return 1;
63 + }
64 +
65 + bool run = true;
66 + netdata_mutex_lock(&ebpf_exit_cleanup);
67 + if (ebpf_plugin_stop())
68 + run = false;
69 + else {
70 + st->thread = thread;
71 + ebpf_module_enabled_set(em, NETDATA_THREAD_EBPF_FUNCTION_RUNNING);
72 + em->lifetime = period;
73 + }
74 + __atomic_store_n(&ctx->run, run, __ATOMIC_RELEASE);
75 + __atomic_store_n(&ctx->ready, true, __ATOMIC_RELEASE);
76 + netdata_mutex_unlock(&ebpf_exit_cleanup);
77 +
78 + if (!run) {
79 + nd_thread_signal_cancel(thread);
80 + nd_thread_join(thread);
81 + return 1;
82 + }
83 +
84 + return 0;
85 }
86
87 /*****************************************************************
@@ -427,26 +475,25 @@ static void ebpf_function_socket_manipulation(
475 }
476 rw_spinlock_write_unlock(&ebpf_judy_pid.index.rw_spinlock);
477
430 - if (em->enabled > NETDATA_THREAD_EBPF_FUNCTION_RUNNING) {
478 + if (ebpf_module_enabled_get(em) > NETDATA_THREAD_EBPF_FUNCTION_RUNNING) {
479 // Cleanup when we already had a thread running
480 rw_spinlock_write_lock(&ebpf_judy_pid.index.rw_spinlock);
481 ebpf_socket_clean_judy_array_unsafe();
482 rw_spinlock_write_unlock(&ebpf_judy_pid.index.rw_spinlock);
483
484 collect_pids |= 1 << EBPF_MODULE_SOCKET_IDX;
437 - netdata_mutex_lock(&ebpf_exit_cleanup);
485 if (ebpf_function_start_thread(em, period)) {
486 ebpf_function_error(transaction, HTTP_RESP_INTERNAL_SERVER_ERROR, "Cannot start thread.");
440 - netdata_mutex_unlock(&ebpf_exit_cleanup);
487 return;
488 }
489 } else {
490 netdata_mutex_lock(&ebpf_exit_cleanup);
491 if (period < 0)
446 - em->lifetime = (em->enabled != NETDATA_THREAD_EBPF_FUNCTION_RUNNING) ? EBPF_NON_FUNCTION_LIFE_TIME :
447 - EBPF_DEFAULT_LIFETIME;
492 + em->lifetime = (ebpf_module_enabled_get(em) != NETDATA_THREAD_EBPF_FUNCTION_RUNNING) ?
493 + EBPF_NON_FUNCTION_LIFE_TIME :
494 + EBPF_DEFAULT_LIFETIME;
495 + netdata_mutex_unlock(&ebpf_exit_cleanup);
496 }
449 - netdata_mutex_unlock(&ebpf_exit_cleanup);
497
498 BUFFER *wb = buffer_create(4096, NULL);
499 buffer_json_initialize(wb, "\"", "\"", 0, true, BUFFER_JSON_OPTIONS_NEWLINE_ON_ARRAY_ITEMS);
src/collectors/ebpf.plugin/ebpf_hardirq.c
+3 -3
@@ -224,7 +224,7 @@ static void hardirq_cleanup(void *pptr)
224 if (!em)
225 return;
226
227 - if (em->enabled == NETDATA_THREAD_EBPF_FUNCTION_RUNNING && !ebpf_plugin_stop()) {
227 + if (ebpf_module_enabled_get(em) == NETDATA_THREAD_EBPF_FUNCTION_RUNNING && !ebpf_plugin_stop()) {
228 netdata_mutex_lock(&lock);
229
230 if (hardirq_safe_clean)
@@ -236,7 +236,7 @@ static void hardirq_cleanup(void *pptr)
236
237 if (!hardirq_safe_clean) {
238 netdata_mutex_lock(&ebpf_exit_cleanup);
239 - em->enabled = NETDATA_THREAD_EBPF_STOPPED;
239 + ebpf_module_enabled_set(em, NETDATA_THREAD_EBPF_STOPPED);
240 netdata_mutex_unlock(&ebpf_exit_cleanup);
241 return;
242 }
@@ -275,7 +275,7 @@ static void hardirq_cleanup(void *pptr)
275 }
276
277 netdata_mutex_lock(&ebpf_exit_cleanup);
278 - em->enabled = NETDATA_THREAD_EBPF_STOPPED;
278 + ebpf_module_enabled_set(em, NETDATA_THREAD_EBPF_STOPPED);
279 netdata_mutex_unlock(&ebpf_exit_cleanup);
280 }
281
src/collectors/ebpf.plugin/ebpf_mdflush.c
+3 -3
@@ -161,12 +161,12 @@ static void mdflush_exit(void *pptr)
161
162 if (!mdflush_safe_clean) {
163 netdata_mutex_lock(&ebpf_exit_cleanup);
164 - em->enabled = NETDATA_THREAD_EBPF_STOPPED;
164 + ebpf_module_enabled_set(em, NETDATA_THREAD_EBPF_STOPPED);
165 netdata_mutex_unlock(&ebpf_exit_cleanup);
166 return;
167 }
168
169 - if (em->enabled == NETDATA_THREAD_EBPF_FUNCTION_RUNNING && !ebpf_plugin_stop()) {
169 + if (ebpf_module_enabled_get(em) == NETDATA_THREAD_EBPF_FUNCTION_RUNNING && !ebpf_plugin_stop()) {
170 netdata_mutex_lock(&lock);
171
172 ebpf_obsolete_mdflush_global(em);
@@ -179,7 +179,7 @@ static void mdflush_exit(void *pptr)
179 em->functions.bpf_unload(em);
180
181 netdata_mutex_lock(&ebpf_exit_cleanup);
182 - em->enabled = NETDATA_THREAD_EBPF_STOPPED;
182 + ebpf_module_enabled_set(em, NETDATA_THREAD_EBPF_STOPPED);
183 netdata_mutex_unlock(&ebpf_exit_cleanup);
184 }
185
src/collectors/ebpf.plugin/ebpf_mount.c
+2 -2
@@ -279,7 +279,7 @@ static void ebpf_mount_exit(void *pptr)
279 if (!em)
280 return;
281
282 - if (em->enabled == NETDATA_THREAD_EBPF_FUNCTION_RUNNING && !ebpf_plugin_stop()) {
282 + if (ebpf_module_enabled_get(em) == NETDATA_THREAD_EBPF_FUNCTION_RUNNING && !ebpf_plugin_stop()) {
283 netdata_mutex_lock(&lock);
284
285 ebpf_obsolete_mount_global(em);
@@ -292,7 +292,7 @@ static void ebpf_mount_exit(void *pptr)
292 em->functions.bpf_unload(em);
293
294 netdata_mutex_lock(&ebpf_exit_cleanup);
295 - em->enabled = NETDATA_THREAD_EBPF_STOPPED;
295 + ebpf_module_enabled_set(em, NETDATA_THREAD_EBPF_STOPPED);
296 netdata_mutex_unlock(&ebpf_exit_cleanup);
297 }
298
src/collectors/ebpf.plugin/ebpf_oomkill.c
+4 -4
@@ -126,7 +126,7 @@ static void oomkill_cleanup(void *pptr)
126 collect_pids &= ~(1 << EBPF_MODULE_OOMKILL_IDX);
127 netdata_mutex_unlock(&lock);
128
129 - if (em->enabled == NETDATA_THREAD_EBPF_FUNCTION_RUNNING && !ebpf_plugin_stop()) {
129 + if (ebpf_module_enabled_get(em) == NETDATA_THREAD_EBPF_FUNCTION_RUNNING && !ebpf_plugin_stop()) {
130 netdata_mutex_lock(&lock);
131
132 if (em->cgroup_charts) {
@@ -143,7 +143,7 @@ static void oomkill_cleanup(void *pptr)
143 em->functions.bpf_unload(em);
144
145 netdata_mutex_lock(&ebpf_exit_cleanup);
146 - em->enabled = NETDATA_THREAD_EBPF_STOPPED;
146 + ebpf_module_enabled_set(em, NETDATA_THREAD_EBPF_STOPPED);
147 netdata_mutex_unlock(&ebpf_exit_cleanup);
148 }
149
@@ -565,14 +565,14 @@ void ebpf_oomkill_thread(void *ptr)
565 // When we are not running integration with apps, we won't fill necessary variables for this thread to run, so
566 // we need to disable it.
567 netdata_mutex_lock(&ebpf_exit_cleanup);
568 - if (em->enabled)
568 + if (ebpf_module_enabled_get(em))
569 netdata_log_info("%s apps integration is completely disabled.", NETDATA_DEFAULT_OOM_DISABLED_MSG);
570 netdata_mutex_unlock(&ebpf_exit_cleanup);
571
572 goto endoomkill;
573 } else if (running_on_kernel < NETDATA_EBPF_KERNEL_4_14) {
574 netdata_mutex_lock(&ebpf_exit_cleanup);
575 - if (em->enabled)
575 + if (ebpf_module_enabled_get(em))
576 netdata_log_info("%s kernel does not have necessary tracepoints.", NETDATA_DEFAULT_OOM_DISABLED_MSG);
577 netdata_mutex_unlock(&ebpf_exit_cleanup);
578
src/collectors/ebpf.plugin/ebpf_process.c
+9 -6
@@ -961,7 +961,7 @@ static void ebpf_process_exit(void *pptr)
961
962 if (!process_safe_clean) {
963 netdata_mutex_lock(&ebpf_exit_cleanup);
964 - em->enabled = NETDATA_THREAD_EBPF_STOPPED;
964 + ebpf_module_enabled_set(em, NETDATA_THREAD_EBPF_STOPPED);
965 netdata_mutex_unlock(&ebpf_exit_cleanup);
966 return;
967 }
@@ -977,7 +977,7 @@ static void ebpf_process_exit(void *pptr)
977 sem_post(shm_mutex_ebpf_integration);
978 }
979
980 - if (em->enabled == NETDATA_THREAD_EBPF_FUNCTION_RUNNING && !ebpf_plugin_stop()) {
980 + if (ebpf_module_enabled_get(em) == NETDATA_THREAD_EBPF_FUNCTION_RUNNING && !ebpf_plugin_stop()) {
981 netdata_mutex_lock(&lock);
982 if (em->cgroup_charts) {
983 ebpf_obsolete_process_cgroup_charts(em);
@@ -1004,7 +1004,7 @@ static void ebpf_process_exit(void *pptr)
1004
1005 netdata_mutex_lock(&ebpf_exit_cleanup);
1006 process_pid_fd = -1;
1007 - em->enabled = NETDATA_THREAD_EBPF_STOPPED;
1007 + ebpf_module_enabled_set(em, NETDATA_THREAD_EBPF_STOPPED);
1008 netdata_mutex_unlock(&ebpf_exit_cleanup);
1009 }
1010
@@ -1801,7 +1801,8 @@ void ebpf_process_thread(void *ptr)
1801 CLEANUP_FUNCTION_REGISTER(ebpf_process_exit) cleanup_ptr = em;
1802
1803 if (!ebpf_module_thread_has_valid_state(em)) {
1804 - em->enabled = em->global_charts = em->apps_charts = em->cgroup_charts = NETDATA_THREAD_EBPF_STOPPING;
1804 + em->global_charts = em->apps_charts = em->cgroup_charts = NETDATA_THREAD_EBPF_STOPPING;
1805 + ebpf_module_enabled_set(em, NETDATA_THREAD_EBPF_STOPPING);
1806 netdata_mutex_lock(&ebpf_exit_cleanup);
1807 ebpf_update_disabled_plugin_stats(em);
1808 netdata_mutex_unlock(&ebpf_exit_cleanup);
@@ -1812,7 +1813,8 @@ void ebpf_process_thread(void *ptr)
1813
1814 netdata_mutex_lock(&ebpf_exit_cleanup);
1815 if (ebpf_process_enable_tracepoints()) {
1815 - em->enabled = em->global_charts = em->apps_charts = em->cgroup_charts = NETDATA_THREAD_EBPF_STOPPING;
1816 + em->global_charts = em->apps_charts = em->cgroup_charts = NETDATA_THREAD_EBPF_STOPPING;
1817 + ebpf_module_enabled_set(em, NETDATA_THREAD_EBPF_STOPPING);
1818 }
1819 netdata_mutex_unlock(&ebpf_exit_cleanup);
1820
@@ -1822,7 +1824,8 @@ void ebpf_process_thread(void *ptr)
1824
1825 set_local_pointers();
1826 if (ebpf_process_load_bpf(em)) {
1825 - em->enabled = em->global_charts = em->apps_charts = em->cgroup_charts = NETDATA_THREAD_EBPF_STOPPING;
1827 + em->global_charts = em->apps_charts = em->cgroup_charts = NETDATA_THREAD_EBPF_STOPPING;
1828 + ebpf_module_enabled_set(em, NETDATA_THREAD_EBPF_STOPPING);
1829 }
1830
1831 int algorithms[NETDATA_KEY_PUBLISH_PROCESS_END] = {
src/collectors/ebpf.plugin/ebpf_shm.c
+2 -2
@@ -488,7 +488,7 @@ static void ebpf_shm_exit(void *pptr)
488 sem_post(shm_mutex_ebpf_integration);
489 }
490
491 - if (em->enabled == NETDATA_THREAD_EBPF_FUNCTION_RUNNING && !ebpf_plugin_stop()) {
491 + if (ebpf_module_enabled_get(em) == NETDATA_THREAD_EBPF_FUNCTION_RUNNING && !ebpf_plugin_stop()) {
492 netdata_mutex_lock(&lock);
493 if (em->cgroup_charts) {
494 ebpf_obsolete_shm_cgroup_charts(em);
@@ -509,7 +509,7 @@ static void ebpf_shm_exit(void *pptr)
509 em->functions.bpf_unload(em);
510
511 netdata_mutex_lock(&ebpf_exit_cleanup);
512 - em->enabled = NETDATA_THREAD_EBPF_STOPPED;
512 + ebpf_module_enabled_set(em, NETDATA_THREAD_EBPF_STOPPED);
513 netdata_mutex_unlock(&ebpf_exit_cleanup);
514 }
515
src/collectors/ebpf.plugin/ebpf_socket.c
+2 -2
@@ -525,7 +525,7 @@ static inline int ebpf_socket_load_and_attach(struct socket_bpf *obj, ebpf_modul
525 static void ebpf_socket_free(ebpf_module_t *em)
526 {
527 netdata_mutex_lock(&ebpf_exit_cleanup);
528 - em->enabled = NETDATA_THREAD_EBPF_STOPPED;
528 + ebpf_module_enabled_set(em, NETDATA_THREAD_EBPF_STOPPED);
529 ebpf_update_stats(&plugin_statistics, em);
530 netdata_mutex_unlock(&ebpf_exit_cleanup);
531
@@ -937,7 +937,7 @@ static void ebpf_socket_exit(void *pptr)
937 sem_post(shm_mutex_ebpf_integration);
938 }
939
940 - if (em->enabled == NETDATA_THREAD_EBPF_FUNCTION_RUNNING && !ebpf_plugin_stop()) {
940 + if (ebpf_module_enabled_get(em) == NETDATA_THREAD_EBPF_FUNCTION_RUNNING && !ebpf_plugin_stop()) {
941 netdata_mutex_lock(&lock);
942
943 if (em->cgroup_charts) {
src/collectors/ebpf.plugin/ebpf_softirq.c
+3 -3
@@ -75,12 +75,12 @@ static void softirq_cleanup(void *pptr)
75
76 if (!softirq_safe_clean) {
77 netdata_mutex_lock(&ebpf_exit_cleanup);
78 - em->enabled = NETDATA_THREAD_EBPF_STOPPED;
78 + ebpf_module_enabled_set(em, NETDATA_THREAD_EBPF_STOPPED);
79 netdata_mutex_unlock(&ebpf_exit_cleanup);
80 return;
81 }
82
83 - if (em->enabled == NETDATA_THREAD_EBPF_FUNCTION_RUNNING && !ebpf_plugin_stop()) {
83 + if (ebpf_module_enabled_get(em) == NETDATA_THREAD_EBPF_FUNCTION_RUNNING && !ebpf_plugin_stop()) {
84 netdata_mutex_lock(&lock);
85
86 ebpf_obsolete_softirq_global(em);
@@ -99,7 +99,7 @@ static void softirq_cleanup(void *pptr)
99 em->functions.bpf_unload(em);
100
101 netdata_mutex_lock(&ebpf_exit_cleanup);
102 - em->enabled = NETDATA_THREAD_EBPF_STOPPED;
102 + ebpf_module_enabled_set(em, NETDATA_THREAD_EBPF_STOPPED);
103 netdata_mutex_unlock(&ebpf_exit_cleanup);
104 }
105
src/collectors/ebpf.plugin/ebpf_swap.c
+3 -3
@@ -459,7 +459,7 @@ static void ebpf_swap_exit(void *pptr)
459 sem_post(shm_mutex_ebpf_integration);
460 }
461
462 - if (em->enabled == NETDATA_THREAD_EBPF_FUNCTION_RUNNING && !ebpf_plugin_stop()) {
462 + if (ebpf_module_enabled_get(em) == NETDATA_THREAD_EBPF_FUNCTION_RUNNING && !ebpf_plugin_stop()) {
463 netdata_mutex_lock(&lock);
464 if (em->cgroup_charts) {
465 ebpf_obsolete_swap_cgroup_charts(em);
@@ -478,7 +478,7 @@ static void ebpf_swap_exit(void *pptr)
478
479 if (!swap_safe_clean) {
480 netdata_mutex_lock(&ebpf_exit_cleanup);
481 - em->enabled = NETDATA_THREAD_EBPF_STOPPED;
481 + ebpf_module_enabled_set(em, NETDATA_THREAD_EBPF_STOPPED);
482 netdata_mutex_unlock(&ebpf_exit_cleanup);
483 return;
484 }
@@ -492,7 +492,7 @@ static void ebpf_swap_exit(void *pptr)
492 em->functions.bpf_unload(em);
493
494 netdata_mutex_lock(&ebpf_exit_cleanup);
495 - em->enabled = NETDATA_THREAD_EBPF_STOPPED;
495 + ebpf_module_enabled_set(em, NETDATA_THREAD_EBPF_STOPPED);
496 netdata_mutex_unlock(&ebpf_exit_cleanup);
497 }
498
src/collectors/ebpf.plugin/ebpf_sync.c
+2 -2
@@ -288,7 +288,7 @@ static void ebpf_sync_exit(void *pptr)
288 if (!em)
289 return;
290
291 - if (em->enabled == NETDATA_THREAD_EBPF_FUNCTION_RUNNING && !ebpf_plugin_stop()) {
291 + if (ebpf_module_enabled_get(em) == NETDATA_THREAD_EBPF_FUNCTION_RUNNING && !ebpf_plugin_stop()) {
292 netdata_mutex_lock(&lock);
293 ebpf_obsolete_sync_global(em);
294 netdata_mutex_unlock(&lock);
@@ -298,7 +298,7 @@ static void ebpf_sync_exit(void *pptr)
298 em->functions.bpf_unload(em);
299
300 netdata_mutex_lock(&ebpf_exit_cleanup);
301 - em->enabled = NETDATA_THREAD_EBPF_STOPPED;
301 + ebpf_module_enabled_set(em, NETDATA_THREAD_EBPF_STOPPED);
302 netdata_mutex_unlock(&ebpf_exit_cleanup);
303 }
304
src/collectors/ebpf.plugin/ebpf_vfs.c
+2 -2
@@ -916,7 +916,7 @@ static void ebpf_vfs_exit(void *pptr)
916 sem_post(shm_mutex_ebpf_integration);
917 }
918
919 - if (em->enabled == NETDATA_THREAD_EBPF_FUNCTION_RUNNING && !ebpf_plugin_stop()) {
919 + if (ebpf_module_enabled_get(em) == NETDATA_THREAD_EBPF_FUNCTION_RUNNING && !ebpf_plugin_stop()) {
920 netdata_mutex_lock(&lock);
921 if (em->cgroup_charts) {
922 ebpf_obsolete_vfs_cgroup_charts(em);
@@ -937,7 +937,7 @@ static void ebpf_vfs_exit(void *pptr)
937 em->functions.bpf_unload(em);
938
939 netdata_mutex_lock(&ebpf_exit_cleanup);
940 - em->enabled = NETDATA_THREAD_EBPF_STOPPED;
940 + ebpf_module_enabled_set(em, NETDATA_THREAD_EBPF_STOPPED);
941 netdata_mutex_unlock(&ebpf_exit_cleanup);
942 }
943
src/collectors/ebpf.plugin/libbpf_api/ebpf.c
+1 -1
@@ -532,7 +532,7 @@ void ebpf_update_stats(ebpf_plugin_stats_t *report, ebpf_module_t *em)
532 int value;
533
534 // It is not necessary to report more information.
535 - if (em->enabled > NETDATA_THREAD_EBPF_FUNCTION_RUNNING)
535 + if (ebpf_module_enabled_get(em) > NETDATA_THREAD_EBPF_FUNCTION_RUNNING)
536 value = -1;
537 else
538 value = 1;
src/collectors/ebpf.plugin/libbpf_api/ebpf_library.c
+4 -4
@@ -409,7 +409,7 @@ void ebpf_set_thread_mode(netdata_run_mode_t lmode)
409
410 void ebpf_enable_specific_chart(ebpf_module_t *em, int disable_cgroup)
411 {
412 - em->enabled = NETDATA_THREAD_EBPF_RUNNING;
412 + ebpf_module_enabled_set(em, NETDATA_THREAD_EBPF_RUNNING);
413
414 if (!disable_cgroup) {
415 em->cgroup_charts = CONFIG_BOOLEAN_YES;
@@ -527,7 +527,7 @@ void read_collector_values(int *disable_cgroups, int update_every, netdata_ebpf_
527
528 network_viewer_opt.enabled = enabled;
529 if (enabled) {
530 - if (!ebpf_modules[EBPF_MODULE_SOCKET_IDX].enabled)
530 + if (!ebpf_module_enabled_get(&ebpf_modules[EBPF_MODULE_SOCKET_IDX]))
531 ebpf_enable_chart(EBPF_MODULE_SOCKET_IDX, *disable_cgroups);
532
533 parse_network_viewer_section(&collector_config);
@@ -1225,7 +1225,7 @@ void ebpf_parse_ips_unsafe(const char *ptr)
1225 */
1226 void ebpf_create_apps_for_module(ebpf_module_t *em, ebpf_target_t *root)
1227 {
1228 - if (em->enabled < NETDATA_THREAD_EBPF_STOPPING && em->apps_charts && em->functions.apps_routine)
1228 + if (ebpf_module_enabled_get(em) < NETDATA_THREAD_EBPF_STOPPING && em->apps_charts && em->functions.apps_routine)
1229 em->functions.apps_routine(em, root);
1230 }
1231
@@ -1609,7 +1609,7 @@ void disable_all_global_charts()
1609 {
1610 int i;
1611 for (i = 0; ebpf_modules[i].info.thread_name; i++) {
1612 - ebpf_modules[i].enabled = NETDATA_THREAD_EBPF_NOT_RUNNING;
1612 + ebpf_module_enabled_set(&ebpf_modules[i], NETDATA_THREAD_EBPF_NOT_RUNNING);
1613 ebpf_modules[i].global_charts = 0;
1614 }
1615 }
src/collectors/systemd-journal.plugin/systemd-journal-files.c
+8 -3
@@ -625,7 +625,12 @@ void nd_journal_directory_scan_recursively(DICTIONARY *files, DICTIONARY *dirs,
625
626 bool existing = false;
627 bool *found = dictionary_set(dirs, dirname, &existing, sizeof(existing));
628 - if (unlikely(!found) || *found) {
628 + if (unlikely(!found)) {
629 + netdata_log_error("Cannot track visited directory '%s' (dictionary_set failed); stopping recursion", dirname);
630 + closedir(dir);
631 + return;
632 + }
633 + if (*found) {
634 closedir(dir);
635 return;
636 }
@@ -639,7 +644,7 @@ void nd_journal_directory_scan_recursively(DICTIONARY *files, DICTIONARY *dirs,
644 ssize_t len = snprintfz(full_path, sizeof(full_path), "%s/%s", dirname, entry->d_name);
645
646 if (entry->d_type == DT_DIR) {
642 - nd_journal_directory_scan_recursively(files, dirs, full_path, depth++);
647 + nd_journal_directory_scan_recursively(files, dirs, full_path, depth + 1);
648 } else if (entry->d_type == DT_REG && is_journal_file(full_path, len, NULL)) {
649 if (files)
650 dictionary_set(files, full_path, NULL, 0);
@@ -654,7 +659,7 @@ void nd_journal_directory_scan_recursively(DICTIONARY *files, DICTIONARY *dirs,
659 // The symbolic link points to a directory
660 char resolved_path[FILENAME_MAX + 1];
661 if (realpath(full_path, resolved_path) != NULL) {
657 - nd_journal_directory_scan_recursively(files, dirs, resolved_path, depth++);
662 + nd_journal_directory_scan_recursively(files, dirs, resolved_path, depth + 1);
663 }
664 } else if (S_ISREG(info.st_mode) && is_journal_file(full_path, len, NULL)) {
665 if (files)
src/libnetdata/dictionary/dictionary-item.h
+1
@@ -482,6 +482,7 @@ static inline DICTIONARY_ITEM *dict_item_add_or_reset_value_and_acquire(DICTIONA
482
483 if(item_check_and_acquire_advanced(dict, item, true) != RC_ITEM_OK) {
484 spins++;
485 + item = NULL;
486 continue;
487 }
488
src/libnetdata/dictionary/dictionary-unittest.c
+39
@@ -935,6 +935,9 @@ size_t dictionary_unittest_views(void) {
935 struct dictionary_stats stats = {};
936 DICTIONARY *master = dictionary_create_advanced(DICT_OPTION_NONE, &stats, 0);
937 DICTIONARY *view = dictionary_create_view(master);
938 + DICTIONARY_ITEM *master_item2 = NULL;
939 + DICTIONARY_ITEM *view_item2 = NULL;
940 + DICTIONARY_ITEM *lookup = NULL;
941
942 fprintf(stderr, "\n\nChecking dictionary views...\n");
943
@@ -1004,6 +1007,42 @@ size_t dictionary_unittest_views(void) {
1007 errors += unittest_check_dictionary("master", master, 0, 0, 1, 0, 1);
1008 errors += unittest_check_dictionary("view", view, 0, 0, 1, 0, 1);
1009
1010 + fprintf(stderr, "\nPASS 3: Replacing a stale view item after master deletion:\n");
1011 + item1_on_master = dictionary_set_and_acquire_item(master, "KEY 1", "VALUE1", strlen("VALUE1") + 1);
1012 + item1_on_view = dictionary_view_set_and_acquire_item(view, "KEY 1 ON VIEW", item1_on_master);
1013 + dictionary_acquired_item_release(view, item1_on_view);
1014 + dictionary_del(master, "KEY 1");
1015 +
1016 + // Suppress the preflight garbage collection once so view_set() exercises
1017 + // the stale-entry cleanup path inside dict_item_add_or_reset_value_and_acquire().
1018 + __atomic_store_n(&view->last_gc_run_us, now_realtime_usec(), __ATOMIC_RELAXED);
1019 +
1020 + master_item2 = dictionary_set_and_acquire_item(master, "KEY 1", "VALUE2", strlen("VALUE2") + 1);
1021 + view_item2 = dictionary_view_set_and_acquire_item(view, "KEY 1 ON VIEW", master_item2);
1022 +
1023 + if(!view_item2) {
1024 + fprintf(stderr, "View replacement returned NULL\n");
1025 + errors++;
1026 + }
1027 + else if(item_flag_check(view_item2, ITEM_FLAG_DELETED) || view_item2->shared != master_item2->shared) {
1028 + fprintf(stderr, "View replacement returned stale/deleted item\n");
1029 + dictionary_acquired_item_release(view, view_item2);
1030 + errors++;
1031 + }
1032 + else
1033 + dictionary_acquired_item_release(view, view_item2);
1034 +
1035 + lookup = (DICTIONARY_ITEM *)dictionary_get_and_acquire_item(view, "KEY 1 ON VIEW");
1036 + if(!lookup || item_flag_check(lookup, ITEM_FLAG_DELETED) || lookup->shared != master_item2->shared) {
1037 + fprintf(stderr, "View lookup did not resolve to the replacement item\n");
1038 + errors++;
1039 + }
1040 + if(lookup)
1041 + dictionary_acquired_item_release(view, lookup);
1042 +
1043 + dictionary_acquired_item_release(master, master_item2);
1044 + dictionary_acquired_item_release(master, item1_on_master);
1045 +
1046 dictionary_destroy(master);
1047 dictionary_destroy(view);
1048 return errors;