@cryptotaxi247 / netdata-1 / commits / 1f0615c14

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();