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;