@cryptotaxi247 / netdata-1 / commits / f284906ba

Improve journal file handling and logging in DBENGINE (#22152)

* Improve journal file handling and logging in DBENGINE - Add datafile acquisition and release for improved resource safety. - Refactor journal header acquisition logic for clarity and robustness. - Enhance error handling and logging for invalid metric lists and page headers. - Ensure proper resource release during early exits or error conditions. * Fix overflow checks in journal file parsing - Add `__builtin_mul_overflow` to validate metric and page list sizes. - Refine boundary checks to prevent exceeding journal file limits. - Improve error handling and logging for invalid file structures.

Stelios Fragkakis committed Apr 10, 2026 at 10:05 UTC f284906ba3a63573b28ecd25198b8c03d88648d8
2 files changed +47 -20
src/database/engine/journalfile.c
+24 -10
@@ -107,9 +107,16 @@ ALWAYS_INLINE struct rrdengine_datafile *njfv2idx_find_and_acquire_j2_header(NJF
107
108 datafile = *PValue;
109
110 - struct rrdengine_journalfile *journalfile = datafile ? datafile->journalfile : NULL;
110 + if (!datafile || !datafile_acquire(datafile, DATAFILE_ACQUIRE_PAGE_DETAILS)) {
111 + datafile = NULL;
112 + PValue = NULL;
113 + continue;
114 + }
115 +
116 + struct rrdengine_journalfile *journalfile = datafile->journalfile;
117
112 - if (!datafile || !journalfile) {
118 + if (!journalfile) {
119 + datafile_release(datafile, DATAFILE_ACQUIRE_PAGE_DETAILS);
120 datafile = NULL;
121 PValue = NULL;
122 continue;
@@ -121,29 +128,36 @@ ALWAYS_INLINE struct rrdengine_datafile *njfv2idx_find_and_acquire_j2_header(NJF
128 s->wanted_end_time_s);
129
130 if(rc == PAGE_IS_IN_RANGE) {
124 - // this is good to return
125 - break;
131 + s->j2_header_acquired = journalfile_v2_data_acquire(journalfile, NULL,
132 + s->wanted_start_time_s,
133 + s->wanted_end_time_s);
134 + if(s->j2_header_acquired) {
135 + // this is good to return
136 + break;
137 + }
138 +
139 + datafile_release(datafile, DATAFILE_ACQUIRE_PAGE_DETAILS);
140 + datafile = NULL;
141 + PValue = NULL;
142 + continue;
143 }
144 else if(rc == PAGE_IS_IN_THE_PAST) {
145 // continue to get the next
146 + datafile_release(datafile, DATAFILE_ACQUIRE_PAGE_DETAILS);
147 datafile = NULL;
148 PValue = NULL;
149 continue;
150 }
151 else /* PAGE_IS_IN_THE_FUTURE */ {
152 // we finished - no more datafiles
153 + datafile_release(datafile, DATAFILE_ACQUIRE_PAGE_DETAILS);
154 datafile = NULL;
155 PValue = NULL;
156 break;
157 }
158 }
159
141 - struct rrdengine_journalfile *journalfile = datafile ? datafile->journalfile : NULL;
142 - if(datafile && journalfile)
143 - s->j2_header_acquired = journalfile_v2_data_acquire(journalfile, NULL,
144 - s->wanted_start_time_s,
145 - s->wanted_end_time_s);
146 - else
160 + if(!datafile)
161 s->j2_header_acquired = NULL;
162
163 rw_spinlock_read_unlock(&s->ctx->njfv2idx.spinlock);
src/database/engine/pagecache.c
+23 -10
@@ -514,8 +514,10 @@ static NOT_INLINE_HOT size_t get_page_list_from_journal_v2(struct rrdengine_inst
514 while((datafile = njfv2idx_find_and_acquire_j2_header(&state))) {
515 struct journal_v2_header *j2_header = state.j2_header_acquired;
516
517 - if (unlikely(!j2_header))
517 + if (unlikely(!j2_header)) {
518 + datafile_release(datafile, DATAFILE_ACQUIRE_PAGE_DETAILS);
519 continue;
520 + }
521
522 char file_path[RRDENG_PATH_MAX];
523 journalfile_v2_generate_path(datafile, file_path, sizeof(file_path));
@@ -531,14 +533,15 @@ static NOT_INLINE_HOT size_t get_page_list_from_journal_v2(struct rrdengine_inst
533 (struct journal_metric_list *)((uint8_t *)j2_header + j2_header->metric_offset);
534 size_t metric_offset = (uint8_t *)uuid_list - (uint8_t *)j2_header;
535
534 - size_t metric_list_size = journal_metric_count * sizeof(*uuid_list);
535 - if (metric_offset + metric_list_size > journal_v2_file_size) {
536 + size_t metric_list_size;
537 + if (__builtin_mul_overflow(journal_metric_count, sizeof(*uuid_list), &metric_list_size) ||
538 + metric_offset > journal_v2_file_size ||
539 + metric_list_size > journal_v2_file_size - metric_offset) {
540 nd_log_limit_static_thread_var(erl, 60, 0);
541 nd_log_limit(&erl, NDLS_DAEMON, NDLP_ERR,
542 "DBENGINE: Metric list exceeds journal file size in journalfile %u of tier %u (metric_offset=%zu, list_size=%zu, file_size=%zu)",
543 datafile->fileno, datafile->tier, metric_offset, metric_list_size, journal_v2_file_size);
540 - journalfile_v2_data_release(datafile->journalfile);
541 - continue;
544 + goto release_journal;
545 }
546
547 struct journal_metric_list *uuid_entry =
@@ -546,20 +549,18 @@ static NOT_INLINE_HOT size_t get_page_list_from_journal_v2(struct rrdengine_inst
549
550 if (unlikely(!uuid_entry)) {
551 // our UUID is not in this datafile
549 - journalfile_v2_data_release(datafile->journalfile);
550 - continue;
552 + goto release_journal;
553 }
554
555 struct journal_page_header *page_list_header =
556 (struct journal_page_header *)((uint8_t *)j2_header + uuid_entry->page_offset);
557 size_t page_offset = (uint8_t *)page_list_header - (uint8_t *)j2_header;
556 - if (page_offset >= journal_v2_file_size) {
558 + if (page_offset > journal_v2_file_size - sizeof(*page_list_header)) {
559 nd_log_limit_static_thread_var(erl, 60, 0);
560 nd_log_limit(&erl, NDLS_DAEMON, NDLP_ERR,
561 "DBENGINE: Invalid page list header in journalfile %u of tier %u",
562 datafile->fileno, datafile->tier);
561 - journalfile_v2_data_release(datafile->journalfile);
562 - continue;
563 + goto release_journal;
564 }
565
566 struct journal_page_list *page_list =
@@ -567,6 +568,16 @@ static NOT_INLINE_HOT size_t get_page_list_from_journal_v2(struct rrdengine_inst
568 struct journal_extent_list *extent_list = (void *)((uint8_t *)j2_header + j2_header->extent_offset);
569 uint32_t extent_entries = j2_header->extent_count;
570 uint32_t uuid_page_entries = page_list_header->entries;
571 + size_t page_list_size;
572 +
573 + if (__builtin_mul_overflow((size_t)uuid_page_entries, sizeof(*page_list), &page_list_size) ||
574 + page_list_size > journal_v2_file_size - page_offset - sizeof(*page_list_header)) {
575 + nd_log_limit_static_thread_var(erl, 60, 0);
576 + nd_log_limit(&erl, NDLS_DAEMON, NDLP_ERR,
577 + "DBENGINE: Page list exceeds journal file size in journalfile %u of tier %u",
578 + datafile->fileno, datafile->tier);
579 + goto release_journal;
580 + }
581
582 for (uint32_t index = 0; index < uuid_page_entries; index++) {
583 struct journal_page_list *page_entry_in_journal = &page_list[index];
@@ -632,7 +643,9 @@ static NOT_INLINE_HOT size_t get_page_list_from_journal_v2(struct rrdengine_inst
643 datafile->fileno, datafile->ctx->config.tier);
644 }
645
646 +release_journal:
647 journalfile_v2_data_release(datafile->journalfile);
648 + datafile_release(datafile, DATAFILE_ACQUIRE_PAGE_DETAILS);
649 }
650
651 return pages_found;