protected access against SIGBUS/SIGSEGV for journal v2 files (#20092)
* protected access against SIGBUS/SIGSEGV for journal v2 files * fix get_page_list_from_journal_v2
Costa Tsaousis committed
Apr 8, 2025 at 22:04 UTC
1f0615c14d87b0ecf1bc0ed06f8407f72ef456a7
8 files changed
+398
-240
CMakeLists.txt
+2
@@ -1294,6 +1294,8 @@ set(DAEMON_FILES
1294
src/daemon/status-file-dmi.h
1295
src/daemon/status-file-product.c
1296
src/daemon/status-file-product.h
1297
+ src/daemon/protected-access.c
1298
+ src/daemon/protected-access.h
1299
)
1300
1301
set(DAEMON_SYSTEMD_WATCHER_FILES
src/daemon/protected-access.c
new
+73
@@ -0,0 +1,73 @@
1
+// SPDX-License-Identifier: GPL-3.0-or-later
2
+
3
+#include "libnetdata/libnetdata.h"
4
+#include "protected-access.h"
5
+
6
+__thread protected_access_t protected_access_state = {0};
7
+
8
+// Declare the thread-local state variable, initialized to zero/inactive.
9
+// *** RELIES ON ASYNC-SIGNAL-SAFE ACCESS TO THIS VARIABLE ***
10
+
11
+// --- Public API Function (called by signal handler) ---
12
+void signal_protected_access_check(int sig, siginfo_t *si, void *context __maybe_unused) {
13
+ // --- ASYNC-SIGNAL-SAFETY WARNING ---
14
+ // The following access to the thread-local 'protected_access'
15
+ // variable MUST be async-signal-safe on your specific target platform.
16
+ // This includes reading is_active, protected_start_addr, protected_size,
17
+ // AND the subsequent call to siglongjmp referencing state->jump_buffer.
18
+ // Standard C/POSIX do NOT guarantee safety for general TLS access here.
19
+ // Use with extreme caution and verify thoroughly.
20
+ // --- END WARNING ---
21
+
22
+ protected_access_t *state = &protected_access_state;
23
+
24
+ // 1. Is protection currently active for *this thread*?
25
+ // Check for state '1' specifically. Don't act if inactive ('0') or jump already happened ('2').
26
+ if (state->is_active != 1)
27
+ return; // Protection not active, handler should ignore.
28
+
29
+ // 2. Is it a signal we want to handle this way?
30
+ // Typically SIGBUS or SIGSEGV for memory access errors.
31
+ if (sig != SIGBUS && sig != SIGSEGV)
32
+ return; // Not a signal we are designed to recover from.
33
+
34
+ // 3. Did the fault occur within the registered protected range?
35
+ // Ensure siginfo_t is valid (it should be if SA_SIGINFO was used)
36
+ if (si == NULL)
37
+ return; // This shouldn't happen if sigaction was set up correctly with SA_SIGINFO
38
+
39
+ void *fault_addr = si->si_addr;
40
+ void *start_addr = state->protected_start_addr;
41
+ // Perform boundary check carefully
42
+ // Check if start_addr is valid before calculation
43
+ if (start_addr == NULL) {
44
+ // State inconsistency? Should not happen if is_active is 1.
45
+ state->is_active = 0; // Attempt reset
46
+ return;
47
+ }
48
+
49
+ // Calculate end address (exclusive)
50
+ void *end_addr = (unsigned char *)start_addr + state->protected_size;
51
+
52
+ if (fault_addr >= start_addr && fault_addr < end_addr) {
53
+ // --- Conditions met! Perform recovery jump ---
54
+
55
+ // Mark that recovery jump is occurring *before* jumping.
56
+ // Set state to '2'. This prevents handler re-entry if another signal occurs
57
+ // immediately, and signals to start() that recovery happened.
58
+ state->is_active = 2;
59
+
60
+ // Jump back to the sigsetjmp point in signal_protected_access_start()
61
+ // The '1' becomes the non-zero return value of sigsetjmp.
62
+ siglongjmp(state->jump_buffer, 1);
63
+
64
+ // --- Execution should not reach here after siglongjmp ---
65
+ // If it somehow did, something is fundamentally broken.
66
+ fprintf(stderr, "FATAL: siglongjmp returned in signal handler!\n");
67
+ abort();
68
+ return; // Should be unreachable
69
+ }
70
+
71
+ // Signal occurred while active, but fault address was outside the protected range.
72
+ // Let the default handler deal with it.
73
+}
src/daemon/protected-access.h
new
+64
@@ -0,0 +1,64 @@
1
+// SPDX-License-Identifier: GPL-3.0-or-later
2
+
3
+#ifndef PROTECTED_ACCESS_H
4
+#define PROTECTED_ACCESS_H
5
+
6
+#include "libnetdata/libnetdata.h"
7
+#include <setjmp.h>
8
+
9
+typedef struct {
10
+ const char *caller;
11
+ sigjmp_buf jump_buffer; // Where to jump back to
12
+ void *protected_start_addr; // Start of the monitored memory range
13
+ size_t protected_size; // Size of the monitored memory range
14
+ // 0=inactive, 1=active (in protected block), 2=jump occurred
15
+ volatile sig_atomic_t is_active; // Must be sig_atomic_t for signal handler safety
16
+} protected_access_t;
17
+
18
+extern __thread protected_access_t protected_access_state;
19
+
20
+#define PROTECTED_ACCESS_START(start, size) ({ \
21
+ bool _rc = false; \
22
+ \
23
+ if (protected_access_state.is_active == 1) \
24
+ fatal("PROTECTED ACCESS: nested PROTECTED_ACCESS_START attempted from " \
25
+ "function %s, while the active is from function %s!", \
26
+ __FUNCTION__, protected_access_state.caller); \
27
+ \
28
+ if (start && size) { \
29
+ protected_access_state.protected_start_addr = start; \
30
+ protected_access_state.protected_size = size; \
31
+ protected_access_state.is_active = 1; \
32
+ protected_access_state.caller = __FUNCTION__; \
33
+ if (sigsetjmp(protected_access_state.jump_buffer, 1) == 0) { \
34
+ /* Initial call successful, sigsetjmp returns 0. */ \
35
+ _rc = true; \
36
+ } else { \
37
+ /* Returned here via siglongjmp from the signal handler. */ \
38
+ /* The handler should have set state->is_active = 2. */ \
39
+ /* Return false to indicate recovery path should be taken. */ \
40
+ _rc = false; \
41
+ } \
42
+ } \
43
+ _rc; \
44
+})
45
+
46
+static inline void protected_access_end(volatile int *ptr __maybe_unused) {
47
+ protected_access_state.is_active = 0;
48
+ protected_access_state.protected_start_addr = NULL;
49
+ protected_access_state.protected_size = 0;
50
+ /* No need to clear jump_buffer explicitly */
51
+}
52
+
53
+#define PROTECTED_ACCESS_AUTO_CLEANUP() \
54
+ volatile int _pa_dummy_cleanup_var __attribute__((cleanup(protected_access_end), unused)) = 0; \
55
+
56
+#define PROTECTED_ACCESS_END() protected_access_end(NULL);
57
+
58
+#define PROTECTED_ACCESS_SETUP(start, size) \
59
+ PROTECTED_ACCESS_AUTO_CLEANUP(); \
60
+ bool no_signal_received = PROTECTED_ACCESS_START(start, size); \
61
+
62
+void signal_protected_access_check(int sig, siginfo_t *si, void *context);
63
+
64
+#endif // PROTECTED_ACCESS_H
src/daemon/signal-handler.c
+2
@@ -2,6 +2,7 @@
2
3
#include "common.h"
4
#include "daemon/status-file.h"
5
+#include "protected-access.h"
6
7
#ifdef ENABLE_SENTRY
8
#include "sentry-native/sentry-native.h"
@@ -49,6 +50,7 @@ static void (*original_sigactions[NSIG])(int, siginfo_t *, void *) = {0};
50
51
NEVER_INLINE
52
void nd_signal_handler(int signo, siginfo_t *info, void *context __maybe_unused) {
53
+ signal_protected_access_check(signo, info, context);
54
55
for(size_t i = 0; i < _countof(signals_waiting) ; i++) {
56
if(signals_waiting[i].signo != signo)
src/database/engine/journalfile.c
+152
-143
@@ -1103,7 +1103,17 @@ int journalfile_v2_load(struct rrdengine_instance *ctx, struct rrdengine_journal
1103
nd_log_daemon(NDLP_DEBUG, "DBENGINE: checking integrity of '%s'", path_v2);
1104
1105
usec_t validation_start_ut = now_monotonic_usec();
1106
- int rc = journalfile_v2_validate(data_start, journal_v2_file_size, journal_v1_file_size);
1106
+
1107
+ int rc = 0;
1108
+ PROTECTED_ACCESS_SETUP(data_start, journal_v2_file_size);
1109
+ if(no_signal_received) {
1110
+ rc = journalfile_v2_validate(data_start, journal_v2_file_size, journal_v1_file_size);
1111
+ }
1112
+ else {
1113
+ nd_log(NDLS_DAEMON, NDLP_ERR, "DBENGINE: failed to access journal file '%s' (SIGBUS)", path_v2);
1114
+ rc = 2;
1115
+ }
1116
+
1117
if (unlikely(rc)) {
1118
if (rc == 2)
1119
error_report("File %s needs to be rebuilt", path_v2);
@@ -1145,6 +1155,7 @@ int journalfile_v2_load(struct rrdengine_instance *ctx, struct rrdengine_journal
1155
1156
if (!db_engine_journal_check)
1157
journalfile->v2.flags |= JOURNALFILE_FLAG_METRIC_CRC_CHECK;
1158
+
1159
journalfile_v2_data_set(journalfile, fd, data_start, journal_v2_file_size);
1160
1161
ctx_current_disk_space_increase(ctx, journal_v2_file_size);
@@ -1351,179 +1362,177 @@ bool journalfile_migrate_to_v2_callback(Word_t section, unsigned datafile_fileno
1362
return false;
1363
}
1364
1354
- fatal_assert(extent_offset <= total_file_size);
1355
- memset(data_start, 0, extent_offset);
1356
-
1357
- // Write header
1358
- struct journal_v2_header j2_header;
1359
- memset(&j2_header, 0, sizeof(j2_header));
1360
-
1361
- j2_header.magic = JOURVAL_V2_MAGIC;
1362
- j2_header.start_time_ut = 0;
1363
- j2_header.end_time_ut = 0;
1364
- j2_header.extent_count = number_of_extents;
1365
- j2_header.extent_offset = extent_offset;
1366
- j2_header.metric_count = number_of_metrics;
1367
- j2_header.metric_offset = metrics_offset;
1368
- j2_header.page_count = number_of_pages;
1369
- j2_header.page_offset = pages_offset;
1370
- j2_header.extent_trailer_offset = extent_offset_trailer;
1371
- j2_header.metric_trailer_offset = metric_offset_trailer;
1372
- j2_header.journal_v2_file_size = total_file_size;
1373
- j2_header.journal_v1_file_size = (uint32_t)journalfile_current_size(journalfile);
1374
- j2_header.data = data_start; // Used during migration
1375
-
1376
- struct journal_v2_block_trailer *journal_v2_trailer;
1377
-
1378
- uint8_t *data = journalfile_v2_write_extent_list(JudyL_extents_pos, data_start + extent_offset);
1379
- internal_error(true, "DBENGINE: write extent list so far %llu", (now_monotonic_usec() - start_loading) / USEC_PER_MS);
1380
-
1381
- fatal_assert(data == data_start + extent_offset_trailer);
1382
-
1383
- // Calculate CRC for extents
1384
- journal_v2_trailer = (struct journal_v2_block_trailer *) (data_start + extent_offset_trailer);
1385
- uLong crc;
1386
- crc = crc32(0L, Z_NULL, 0);
1387
- crc = crc32(crc, (uint8_t *) data_start + extent_offset, number_of_extents * sizeof(struct journal_extent_list));
1388
- crc32set(journal_v2_trailer->checksum, crc);
1389
-
1390
- internal_error(true, "DBENGINE: CALCULATE CRC FOR EXTENT %llu", (now_monotonic_usec() - start_loading) / USEC_PER_MS);
1391
- // Skip the trailer, point to the metrics off
1392
- data += sizeof(struct journal_v2_block_trailer);
1365
+ struct journal_metric_list_to_sort *uuid_list = NULL;
1366
+
1367
+ PROTECTED_ACCESS_SETUP(data_start, total_file_size);
1368
+ if(no_signal_received) {
1369
+ fatal_assert(extent_offset <= total_file_size);
1370
+ memset(data_start, 0, extent_offset);
1371
+
1372
+ // Write header
1373
+ struct journal_v2_header j2_header;
1374
+ memset(&j2_header, 0, sizeof(j2_header));
1375
+
1376
+ j2_header.magic = JOURVAL_V2_MAGIC;
1377
+ j2_header.start_time_ut = 0;
1378
+ j2_header.end_time_ut = 0;
1379
+ j2_header.extent_count = number_of_extents;
1380
+ j2_header.extent_offset = extent_offset;
1381
+ j2_header.metric_count = number_of_metrics;
1382
+ j2_header.metric_offset = metrics_offset;
1383
+ j2_header.page_count = number_of_pages;
1384
+ j2_header.page_offset = pages_offset;
1385
+ j2_header.extent_trailer_offset = extent_offset_trailer;
1386
+ j2_header.metric_trailer_offset = metric_offset_trailer;
1387
+ j2_header.journal_v2_file_size = total_file_size;
1388
+ j2_header.journal_v1_file_size = (uint32_t)journalfile_current_size(journalfile);
1389
+ j2_header.data = data_start; // Used during migration
1390
+
1391
+ struct journal_v2_block_trailer *journal_v2_trailer;
1392
+
1393
+ uint8_t *data = journalfile_v2_write_extent_list(JudyL_extents_pos, data_start + extent_offset);
1394
+ internal_error(
1395
+ true, "DBENGINE: write extent list so far %llu", (now_monotonic_usec() - start_loading) / USEC_PER_MS);
1396
+
1397
+ fatal_assert(data == data_start + extent_offset_trailer);
1398
+
1399
+ // Calculate CRC for extents
1400
+ journal_v2_trailer = (struct journal_v2_block_trailer *)(data_start + extent_offset_trailer);
1401
+ uLong crc;
1402
+ crc = crc32(0L, Z_NULL, 0);
1403
+ crc = crc32(crc, (uint8_t *)data_start + extent_offset, number_of_extents * sizeof(struct journal_extent_list));
1404
+ crc32set(journal_v2_trailer->checksum, crc);
1405
1394
- // Sanity check -- we must be at the metrics_offset
1395
- fatal_assert(data == data_start + metrics_offset);
1406
+ internal_error(
1407
+ true, "DBENGINE: CALCULATE CRC FOR EXTENT %llu", (now_monotonic_usec() - start_loading) / USEC_PER_MS);
1408
+ // Skip the trailer, point to the metrics off
1409
+ data += sizeof(struct journal_v2_block_trailer);
1410
1397
- // Allocate array to sort UUIDs and keep them sorted in the journal because we want to do binary search when we do lookups
1398
- struct journal_metric_list_to_sort *uuid_list = mallocz(number_of_metrics * sizeof(struct journal_metric_list_to_sort));
1411
+ // Sanity check -- we must be at the metrics_offset
1412
+ fatal_assert(data == data_start + metrics_offset);
1413
1400
- Word_t Index = 0;
1401
- size_t count = 0;
1402
- bool first_then_next = true;
1403
- while ((PValue = JudyLFirstThenNext(JudyL_metrics, &Index, &first_then_next))) {
1404
- metric_info = *PValue;
1405
-
1406
- fatal_assert(metric_info != NULL);
1407
- fatal_assert(count < number_of_metrics);
1408
- uuid_list[count++].metric_info = metric_info;
1409
- min_time_s = MIN(min_time_s, metric_info->first_time_s);
1410
- max_time_s = MAX(max_time_s, metric_info->last_time_s);
1411
- }
1414
+ // Allocate array to sort UUIDs and keep them sorted in the journal because we want to do binary search when we do lookups
1415
+ uuid_list = mallocz(number_of_metrics * sizeof(struct journal_metric_list_to_sort));
1416
1413
- fatal_assert(count == number_of_metrics);
1417
+ Word_t Index = 0;
1418
+ size_t count = 0;
1419
+ bool first_then_next = true;
1420
+ while ((PValue = JudyLFirstThenNext(JudyL_metrics, &Index, &first_then_next))) {
1421
+ metric_info = *PValue;
1422
1415
- // Check if not properly set in the loop above to prevent overflow
1416
- if (min_time_s == LONG_MAX)
1417
- min_time_s = 0;
1423
+ fatal_assert(metric_info != NULL);
1424
+ fatal_assert(count < number_of_metrics);
1425
+ uuid_list[count++].metric_info = metric_info;
1426
+ min_time_s = MIN(min_time_s, metric_info->first_time_s);
1427
+ max_time_s = MAX(max_time_s, metric_info->last_time_s);
1428
+ }
1429
1419
- // Store in the header
1420
- j2_header.start_time_ut = min_time_s * USEC_PER_SEC;
1421
- j2_header.end_time_ut = max_time_s * USEC_PER_SEC;
1430
+ fatal_assert(count == number_of_metrics);
1431
1423
- qsort(&uuid_list[0], number_of_metrics, sizeof(struct journal_metric_list_to_sort), journalfile_metric_compare);
1424
- internal_error(true, "DBENGINE: traverse and qsort UUID %llu", (now_monotonic_usec() - start_loading) / USEC_PER_MS);
1432
+ // Check if not properly set in the loop above to prevent overflow
1433
+ if (min_time_s == LONG_MAX)
1434
+ min_time_s = 0;
1435
1426
- uint32_t resize_file_to = total_file_size;
1436
+ // Store in the header
1437
+ j2_header.start_time_ut = min_time_s * USEC_PER_SEC;
1438
+ j2_header.end_time_ut = max_time_s * USEC_PER_SEC;
1439
1428
- for (Index = 0; Index < number_of_metrics; Index++) {
1429
- metric_info = uuid_list[Index].metric_info;
1440
+ qsort(&uuid_list[0], number_of_metrics, sizeof(struct journal_metric_list_to_sort), journalfile_metric_compare);
1441
+ internal_error(
1442
+ true, "DBENGINE: traverse and qsort UUID %llu", (now_monotonic_usec() - start_loading) / USEC_PER_MS);
1443
1431
- // Calculate current UUID offset from start of file. We will store this in the data page header
1432
- uint32_t uuid_offset = data - data_start;
1444
+ for (Index = 0; Index < number_of_metrics; Index++) {
1445
+ metric_info = uuid_list[Index].metric_info;
1446
1434
- struct journal_metric_list *current_metric = (void *) data;
1435
- // Write the UUID we are processing
1436
- data = (void *) journalfile_v2_write_metric_page(&j2_header, data, metric_info, pages_offset);
1437
- if (unlikely(!data))
1438
- break;
1447
+ // Calculate current UUID offset from start of file. We will store this in the data page header
1448
+ uint32_t uuid_offset = data - data_start;
1449
1440
- // Next we will write
1441
- // Header
1442
- // Detailed entries (descr @ time)
1443
- // Trailer (checksum)
1450
+ struct journal_metric_list *current_metric = (void *)data;
1451
+ // Write the UUID we are processing
1452
+ data = (void *)journalfile_v2_write_metric_page(&j2_header, data, metric_info, pages_offset);
1453
+ if (unlikely(!data))
1454
+ break;
1455
1445
- // Keep the page_list_header, to be used for migration when where agent is running
1446
- metric_info->page_list_header = pages_offset;
1447
- // Write page header
1448
- void *metric_page = journalfile_v2_write_data_page_header(&j2_header, data_start + pages_offset, metric_info,
1449
- uuid_offset);
1456
+ // Next we will write
1457
+ // Header
1458
+ // Detailed entries (descr @ time)
1459
+ // Trailer (checksum)
1460
1451
- // Start writing descr @ time
1452
- void *page_trailer = journalfile_v2_write_descriptors(&j2_header, metric_page, metric_info, current_metric);
1453
- if (unlikely(!page_trailer))
1454
- break;
1461
+ // Keep the page_list_header, to be used for migration when where agent is running
1462
+ metric_info->page_list_header = pages_offset;
1463
+ // Write page header
1464
+ void *metric_page =
1465
+ journalfile_v2_write_data_page_header(&j2_header, data_start + pages_offset, metric_info, uuid_offset);
1466
1456
- // Trailer (checksum)
1457
- uint8_t *next_page_address = journalfile_v2_write_data_page_trailer(&j2_header, page_trailer,
1458
- data_start + pages_offset);
1467
+ // Start writing descr @ time
1468
+ void *page_trailer = journalfile_v2_write_descriptors(&j2_header, metric_page, metric_info, current_metric);
1469
+ if (unlikely(!page_trailer))
1470
+ break;
1471
1460
- // Calculate start of the pages start for next descriptor
1461
- pages_offset += (metric_info->number_of_pages * (sizeof(struct journal_page_list)) + sizeof(struct journal_page_header) + sizeof(struct journal_v2_block_trailer));
1462
- // Verify we are at the right location
1463
- if (pages_offset != (uint32_t)(next_page_address - data_start)) {
1464
- // make sure checks fail so that we abort
1465
- data = data_start;
1466
- break;
1472
+ // Trailer (checksum)
1473
+ uint8_t *next_page_address =
1474
+ journalfile_v2_write_data_page_trailer(&j2_header, page_trailer, data_start + pages_offset);
1475
+
1476
+ // Calculate start of the pages start for next descriptor
1477
+ pages_offset +=
1478
+ (metric_info->number_of_pages * (sizeof(struct journal_page_list)) +
1479
+ sizeof(struct journal_page_header) + sizeof(struct journal_v2_block_trailer));
1480
+ // Verify we are at the right location
1481
+ if (pages_offset != (uint32_t)(next_page_address - data_start)) {
1482
+ // make sure checks fail so that we abort
1483
+ data = data_start;
1484
+ break;
1485
+ }
1486
}
1468
- }
1487
1470
- if (data == data_start + metric_offset_trailer) {
1471
- internal_error(true, "DBENGINE: WRITE METRICS AND PAGES %llu", (now_monotonic_usec() - start_loading) / USEC_PER_MS);
1488
+ if (data == data_start + metric_offset_trailer) {
1489
+ internal_error(
1490
+ true, "DBENGINE: WRITE METRICS AND PAGES %llu", (now_monotonic_usec() - start_loading) / USEC_PER_MS);
1491
1473
- // Calculate CRC for metrics
1474
- journal_v2_trailer = (struct journal_v2_block_trailer *)(data_start + metric_offset_trailer);
1475
- crc = crc32(0L, Z_NULL, 0);
1476
- crc =
1477
- crc32(crc, (uint8_t *)data_start + metrics_offset, number_of_metrics * sizeof(struct journal_metric_list));
1478
- crc32set(journal_v2_trailer->checksum, crc);
1479
- internal_error(true, "DBENGINE: CALCULATE CRC FOR UUIDs %llu", (now_monotonic_usec() - start_loading) / USEC_PER_MS);
1480
-
1481
- // Prepare to write checksum for the file
1482
- j2_header.data = NULL;
1483
- journal_v2_trailer = (struct journal_v2_block_trailer *)(data_start + trailer_offset);
1484
- crc = crc32(0L, Z_NULL, 0);
1485
- crc = crc32(crc, (void *)&j2_header, sizeof(j2_header));
1486
- crc32set(journal_v2_trailer->checksum, crc);
1492
+ // Calculate CRC for metrics
1493
+ journal_v2_trailer = (struct journal_v2_block_trailer *)(data_start + metric_offset_trailer);
1494
+ crc = crc32(0L, Z_NULL, 0);
1495
+ crc = crc32(
1496
+ crc, (uint8_t *)data_start + metrics_offset, number_of_metrics * sizeof(struct journal_metric_list));
1497
+ crc32set(journal_v2_trailer->checksum, crc);
1498
+ internal_error(
1499
+ true, "DBENGINE: CALCULATE CRC FOR UUIDs %llu", (now_monotonic_usec() - start_loading) / USEC_PER_MS);
1500
+
1501
+ // Prepare to write checksum for the file
1502
+ j2_header.data = NULL;
1503
+ journal_v2_trailer = (struct journal_v2_block_trailer *)(data_start + trailer_offset);
1504
+ crc = crc32(0L, Z_NULL, 0);
1505
+ crc = crc32(crc, (void *)&j2_header, sizeof(j2_header));
1506
+ crc32set(journal_v2_trailer->checksum, crc);
1507
1488
- // Write header to the file
1489
- memcpy(data_start, &j2_header, sizeof(j2_header));
1508
+ // Write header to the file
1509
+ memcpy(data_start, &j2_header, sizeof(j2_header));
1510
1491
- internal_error(true, "DBENGINE: FILE COMPLETED --------> %llu", (now_monotonic_usec() - start_loading) / USEC_PER_MS);
1511
+ internal_error(
1512
+ true, "DBENGINE: FILE COMPLETED --------> %llu", (now_monotonic_usec() - start_loading) / USEC_PER_MS);
1513
1493
- netdata_log_info("DBENGINE: migrated journal file '%s', file size %zu", path, total_file_size);
1514
+ netdata_log_info("DBENGINE: migrated journal file '%s', file size %zu", path, total_file_size);
1515
1495
- // msync(data_start, total_file_size, MS_SYNC);
1496
- journalfile_v2_data_set(journalfile, fd_v2, data_start, total_file_size);
1516
+ // msync(data_start, total_file_size, MS_SYNC);
1517
+ journalfile_v2_data_set(journalfile, fd_v2, data_start, total_file_size);
1518
1498
- internal_error(true, "DBENGINE: ACTIVATING NEW INDEX JNL %llu", (now_monotonic_usec() - start_loading) / USEC_PER_MS);
1499
- ctx_current_disk_space_increase(ctx, total_file_size);
1500
- freez(uuid_list);
1501
- return true;
1519
+ internal_error(
1520
+ true, "DBENGINE: ACTIVATING NEW INDEX JNL %llu", (now_monotonic_usec() - start_loading) / USEC_PER_MS);
1521
+ ctx_current_disk_space_increase(ctx, total_file_size);
1522
+ return true;
1523
+ }
1524
}
1525
else {
1504
- netdata_log_info("DBENGINE: failed to build index '%s', file will be skipped", path);
1505
- j2_header.data = NULL;
1506
- j2_header.magic = JOURVAL_V2_SKIP_MAGIC;
1507
- memcpy(data_start, &j2_header, sizeof(j2_header));
1508
- resize_file_to = sizeof(j2_header);
1526
+ nd_log(NDLS_DAEMON, NDLP_ERR, "DBENGINE: failed to write journal file '%s' (SIGBUS)", path);
1527
}
1528
1511
- nd_munmap(data_start, total_file_size);
1529
freez(uuid_list);
1530
1514
- if (likely(resize_file_to == total_file_size))
1515
- return true;
1516
-
1517
- int ret = truncate(path, (long) resize_file_to);
1518
- if (ret < 0) {
1519
- ctx_current_disk_space_increase(ctx, total_file_size);
1520
- ctx_fs_error(ctx);
1521
- netdata_log_error("DBENGINE: failed to resize file '%s'", path);
1522
- }
1523
- else
1524
- ctx_current_disk_space_increase(ctx, resize_file_to);
1531
+ netdata_log_info("DBENGINE: failed to build index '%s', file will be skipped", path);
1532
1526
- return true;
1533
+ nd_munmap(data_start, total_file_size);
1534
+ unlink(path);
1535
+ return false;
1536
}
1537
1538
int journalfile_load(struct rrdengine_instance *ctx, struct rrdengine_journalfile *journalfile,
src/database/engine/pagecache.c
+100
-80
@@ -488,7 +488,7 @@ static ALWAYS_INLINE_HOT size_t list_has_time_gaps(
488
// ----------------------------------------------------------------------------
489
490
typedef void (*page_found_callback_t)(PGC_PAGE *page, void *data);
491
-static ALWAYS_INLINE_HOT size_t get_page_list_from_journal_v2(struct rrdengine_instance *ctx, METRIC *metric, usec_t start_time_ut, usec_t end_time_ut, page_found_callback_t callback, void *callback_data) {
491
+static NOT_INLINE_HOT size_t get_page_list_from_journal_v2(struct rrdengine_instance *ctx, METRIC *metric, usec_t start_time_ut, usec_t end_time_ut, page_found_callback_t callback, void *callback_data) {
492
nd_uuid_t *uuid = mrg_metric_uuid(main_mrg, metric);
493
Word_t metric_id = mrg_metric_id(main_mrg, metric);
494
@@ -513,99 +513,119 @@ static ALWAYS_INLINE_HOT size_t get_page_list_from_journal_v2(struct rrdengine_i
513
if (unlikely(!j2_header))
514
continue;
515
516
- time_t journal_start_time_s = (time_t)(j2_header->start_time_ut / USEC_PER_SEC);
517
- size_t journal_v2_file_size = datafile->journalfile->mmap.size;
516
+ PROTECTED_ACCESS_SETUP(datafile->journalfile->mmap.data, datafile->journalfile->mmap.size);
517
+ if(no_signal_received) {
518
+ time_t journal_start_time_s = (time_t)(j2_header->start_time_ut / USEC_PER_SEC);
519
+ size_t journal_v2_file_size = datafile->journalfile->mmap.size;
520
519
- // the datafile possibly contains useful data for this query
521
+ // the datafile possibly contains useful data for this query
522
521
- size_t journal_metric_count = (size_t)j2_header->metric_count;
522
- struct journal_metric_list *uuid_list = (struct journal_metric_list *)((uint8_t *) j2_header + j2_header->metric_offset);
523
- size_t metric_offset = (uint8_t *) uuid_list - (uint8_t *) j2_header;
524
- if (metric_offset >= journal_v2_file_size) {
525
- nd_log_limit_static_thread_var(erl, 60, 0);
526
- nd_log_limit(&erl, NDLS_DAEMON, NDLP_ERR, "DBENGINE: Invalid metric list header in journalfile %u of tier %u", datafile->fileno, datafile->tier);
527
- journalfile_v2_data_release(datafile->journalfile);
528
- continue;
529
- }
523
+ size_t journal_metric_count = (size_t)j2_header->metric_count;
524
+ struct journal_metric_list *uuid_list =
525
+ (struct journal_metric_list *)((uint8_t *)j2_header + j2_header->metric_offset);
526
+ size_t metric_offset = (uint8_t *)uuid_list - (uint8_t *)j2_header;
527
+ if (metric_offset >= journal_v2_file_size) {
528
+ nd_log_limit_static_thread_var(erl, 60, 0);
529
+ nd_log_limit(&erl, NDLS_DAEMON, NDLP_ERR,
530
+ "DBENGINE: Invalid metric list header in journalfile %u of tier %u",
531
+ datafile->fileno, datafile->tier);
532
+ journalfile_v2_data_release(datafile->journalfile);
533
+ continue;
534
+ }
535
531
- struct journal_metric_list *uuid_entry = bsearch(uuid,uuid_list,journal_metric_count,sizeof(*uuid_list), journal_metric_uuid_compare);
536
+ struct journal_metric_list *uuid_entry =
537
+ bsearch(uuid, uuid_list, journal_metric_count, sizeof(*uuid_list), journal_metric_uuid_compare);
538
533
- if (unlikely(!uuid_entry)) {
534
- // our UUID is not in this datafile
535
- journalfile_v2_data_release(datafile->journalfile);
536
- continue;
537
- }
539
+ if (unlikely(!uuid_entry)) {
540
+ // our UUID is not in this datafile
541
+ journalfile_v2_data_release(datafile->journalfile);
542
+ continue;
543
+ }
544
539
- struct journal_page_header *page_list_header = (struct journal_page_header *) ((uint8_t *) j2_header + uuid_entry->page_offset);
540
- size_t page_offset = (uint8_t *) page_list_header - (uint8_t *) j2_header;
541
- if (page_offset >= journal_v2_file_size) {
542
- nd_log_limit_static_thread_var(erl, 60, 0);
543
- nd_log_limit(&erl, NDLS_DAEMON, NDLP_ERR, "DBENGINE: Invalid page list header in journalfile %u of tier %u", datafile->fileno, datafile->tier);
544
- journalfile_v2_data_release(datafile->journalfile);
545
- continue;
546
- }
545
+ struct journal_page_header *page_list_header =
546
+ (struct journal_page_header *)((uint8_t *)j2_header + uuid_entry->page_offset);
547
+ size_t page_offset = (uint8_t *)page_list_header - (uint8_t *)j2_header;
548
+ if (page_offset >= journal_v2_file_size) {
549
+ nd_log_limit_static_thread_var(erl, 60, 0);
550
+ nd_log_limit(&erl, NDLS_DAEMON, NDLP_ERR,
551
+ "DBENGINE: Invalid page list header in journalfile %u of tier %u",
552
+ datafile->fileno, datafile->tier);
553
+ journalfile_v2_data_release(datafile->journalfile);
554
+ continue;
555
+ }
556
548
- struct journal_page_list *page_list = (struct journal_page_list *)((uint8_t *) page_list_header + sizeof(*page_list_header));
549
- struct journal_extent_list *extent_list = (void *)((uint8_t *)j2_header + j2_header->extent_offset);
550
- uint32_t extent_entries = j2_header->extent_count;
551
- uint32_t uuid_page_entries = page_list_header->entries;
557
+ struct journal_page_list *page_list =
558
+ (struct journal_page_list *)((uint8_t *)page_list_header + sizeof(*page_list_header));
559
+ struct journal_extent_list *extent_list = (void *)((uint8_t *)j2_header + j2_header->extent_offset);
560
+ uint32_t extent_entries = j2_header->extent_count;
561
+ uint32_t uuid_page_entries = page_list_header->entries;
562
553
- for (uint32_t index = 0; index < uuid_page_entries; index++) {
554
- struct journal_page_list *page_entry_in_journal = &page_list[index];
563
+ for (uint32_t index = 0; index < uuid_page_entries; index++) {
564
+ struct journal_page_list *page_entry_in_journal = &page_list[index];
565
556
- time_t page_first_time_s = page_entry_in_journal->delta_start_s + journal_start_time_s;
557
- time_t page_last_time_s = page_entry_in_journal->delta_end_s + journal_start_time_s;
566
+ time_t page_first_time_s = page_entry_in_journal->delta_start_s + journal_start_time_s;
567
+ time_t page_last_time_s = page_entry_in_journal->delta_end_s + journal_start_time_s;
568
559
- TIME_RANGE_COMPARE prc = is_page_in_time_range(page_first_time_s, page_last_time_s, wanted_start_time_s, wanted_end_time_s);
560
- if(prc == PAGE_IS_IN_THE_PAST)
561
- continue;
569
+ TIME_RANGE_COMPARE prc =
570
+ is_page_in_time_range(page_first_time_s, page_last_time_s, wanted_start_time_s, wanted_end_time_s);
571
563
- if(prc == PAGE_IS_IN_THE_FUTURE)
564
- break;
572
+ if (prc == PAGE_IS_IN_THE_PAST)
573
+ continue;
574
566
- // Make sure index is valid for this file
567
- if (page_entry_in_journal->extent_index > extent_entries) {
568
- nd_log_limit_static_thread_var(erl, 60, 0);
569
- nd_log_limit(&erl, NDLS_DAEMON, NDLP_ERR, "DBENGINE: Invalid extent index in journalfile %u", datafile->fileno);
570
- break;
571
- }
575
+ if (prc == PAGE_IS_IN_THE_FUTURE)
576
+ break;
577
+
578
+ // Make sure index is valid for this file
579
+ if (page_entry_in_journal->extent_index > extent_entries) {
580
+ nd_log_limit_static_thread_var(erl, 60, 0);
581
+ nd_log_limit(&erl, NDLS_DAEMON, NDLP_ERR,
582
+ "DBENGINE: Invalid extent index in journalfile %u",
583
+ datafile->fileno);
584
+ break;
585
+ }
586
573
- uint32_t page_update_every_s = page_entry_in_journal->update_every_s;
574
- size_t page_length = page_entry_in_journal->page_length;
575
-
576
- if(datafile_acquire(datafile, DATAFILE_ACQUIRE_OPEN_CACHE)) { //for open cache item
577
- // add this page to open cache
578
- bool added = false;
579
- struct extent_io_data ei = {
580
- .pos = extent_list[page_entry_in_journal->extent_index].datafile_offset,
581
- .bytes = extent_list[page_entry_in_journal->extent_index].datafile_size,
582
- .page_length = page_length,
583
- .file = datafile->file,
584
- .fileno = datafile->fileno,
585
- };
586
-
587
- PGC_PAGE *page = pgc_page_add_and_acquire(open_cache, (PGC_ENTRY) {
588
- .hot = false,
589
- .section = (Word_t) ctx,
590
- .metric_id = metric_id,
591
- .start_time_s = page_first_time_s,
592
- .end_time_s = page_last_time_s,
593
- .update_every_s = page_update_every_s,
594
- .data = datafile,
595
- .size = 0,
596
- .custom_data = (uint8_t *) &ei,
597
- }, &added);
598
-
599
- if(!added)
600
- datafile_release(datafile, DATAFILE_ACQUIRE_OPEN_CACHE);
601
-
602
- callback(page, callback_data);
603
-
604
- pgc_page_release(open_cache, page);
605
-
606
- pages_found++;
587
+ uint32_t page_update_every_s = page_entry_in_journal->update_every_s;
588
+ size_t page_length = page_entry_in_journal->page_length;
589
+
590
+ if (datafile_acquire(datafile, DATAFILE_ACQUIRE_OPEN_CACHE)) {
591
+ //for open cache item
592
+ // add this page to open cache
593
+ bool added = false;
594
+ struct extent_io_data ei = {0};
595
+ ei.pos = extent_list[page_entry_in_journal->extent_index].datafile_offset;
596
+ ei.bytes = extent_list[page_entry_in_journal->extent_index].datafile_size;
597
+ ei.page_length = page_length;
598
+ ei.file = datafile->file;
599
+ ei.fileno = datafile->fileno;
600
+
601
+ PGC_ENTRY e = {0};
602
+ e.hot = false;
603
+ e.section = (Word_t)ctx;
604
+ e.metric_id = metric_id;
605
+ e.start_time_s = page_first_time_s;
606
+ e.end_time_s = page_last_time_s;
607
+ e.update_every_s = page_update_every_s;
608
+ e.data = datafile;
609
+ e.size = 0;
610
+ e.custom_data = (uint8_t *)&ei;
611
+ PGC_PAGE *page = pgc_page_add_and_acquire(open_cache, e, &added);
612
+
613
+ if (!added)
614
+ datafile_release(datafile, DATAFILE_ACQUIRE_OPEN_CACHE);
615
+
616
+ callback(page, callback_data);
617
+
618
+ pgc_page_release(open_cache, page);
619
+
620
+ pages_found++;
621
+ }
622
}
623
}
624
+ else {
625
+ nd_log(NDLS_DAEMON, NDLP_ERR,
626
+ "DBENGINE: failed to journal file %u of tier %u (SIGBUS)",
627
+ datafile->fileno, datafile->tier);
628
+ }
629
630
journalfile_v2_data_release(datafile->journalfile);
631
}
src/database/engine/rrdengine.h
+2
@@ -20,6 +20,8 @@
20
#include "pdc.h"
21
#include "page.h"
22
23
+#include "daemon/protected-access.h"
24
+
25
extern unsigned rrdeng_pages_per_extent;
26
27
/* Forward declarations */
src/libnetdata/log/nd_log.c
+3
-17
@@ -519,23 +519,9 @@ void netdata_logger_fatal(const char *file, const char *function, const unsigned
519
va_end(args);
520
}
521
522
- char date[LOG_DATE_LENGTH];
523
- log_date(date, LOG_DATE_LENGTH, now_realtime_sec());
524
-
525
- char action_data[70+1];
526
- snprintfz(action_data, 70, "%04lu@%-10.10s:%-15.15s/%d", line, file, function, saved_errno);
527
-
528
- const char *thread_tag = nd_thread_tag();
529
- const char *tag_to_send = thread_tag;
530
-
531
- // anonymize thread names
532
- if(strncmp(thread_tag, THREAD_TAG_STREAM_RECEIVER, strlen(THREAD_TAG_STREAM_RECEIVER)) == 0)
533
- tag_to_send = THREAD_TAG_STREAM_RECEIVER;
534
- if(strncmp(thread_tag, THREAD_TAG_STREAM_SENDER, strlen(THREAD_TAG_STREAM_SENDER)) == 0)
535
- tag_to_send = THREAD_TAG_STREAM_SENDER;
536
-
537
- char action_result[200+1];
538
- snprintfz(action_result, 60, "%s:%s:%s", program_name, tag_to_send, function);
522
+#if defined(FSANITIZE_ADDRESS)
523
+ fprintf(stderr, "FATAL: %04lu@%s:%s, errno = %d\n", line, file, function, saved_errno);
524
+#endif
525
526
#ifdef NETDATA_INTERNAL_CHECKS
527
fatal_abort_internal_checks();