master
c 124 lines 4.97 KB
Raw
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 // Helper function to get diagnostic information from the last fault
12 const protected_access_frame_t *protected_access_get_last_fault(void) {
13 if (protected_access_state.depth < 1)
14 return NULL;
15
16 protected_access_frame_t *frame = &protected_access_state.stack[protected_access_state.depth-1];
17 if (frame->is_active != 2) // Not a frame with a fault
18 return NULL;
19
20 return frame;
21 }
22
23 // Format a string with diagnostic information about the last fault
24 void protected_access_format_error(char *buffer, size_t buffer_size) {
25 const protected_access_frame_t *frame = protected_access_get_last_fault();
26 if (!frame) {
27 snprintf(buffer, buffer_size, "No protected access fault information available");
28 return;
29 }
30
31 // Use the proper public API for signal code formatting
32 char signal_code_buf[128];
33 SIGNAL_CODE_2str_h(frame->signal_code, signal_code_buf, sizeof(signal_code_buf));
34
35 snprintf(buffer, buffer_size,
36 "Protected access fault in %s: %s %s failed with signal %s\n"
37 " Fault address: %p (offset +%lu within protected region %p-%p)",
38 frame->caller,
39 frame->operation,
40 frame->resource_name,
41 signal_code_buf,
42 frame->fault_address,
43 (unsigned long)((char*)frame->fault_address - (char*)frame->protected_start_addr),
44 frame->protected_start_addr,
45 (void*)((char*)frame->protected_start_addr + frame->protected_size)
46 );
47 }
48
49 // --- Public API Function (called by signal handler) ---
50 void signal_protected_access_check(int sig, siginfo_t *si, void *context __maybe_unused) {
51 // --- ASYNC-SIGNAL-SAFETY WARNING ---
52 // The following access to the thread-local 'protected_access'
53 // variable MUST be async-signal-safe on your specific target platform.
54 // This includes reading is_active, protected_start_addr, protected_size,
55 // AND the subsequent call to siglongjmp referencing state->jump_buffer.
56 // Standard C/POSIX do NOT guarantee safety for general TLS access here.
57 // Use with extreme caution and verify thoroughly.
58 // --- END WARNING ---
59
60 protected_access_t *state = &protected_access_state;
61
62 // Make sure we have active frames
63 if (state->depth == 0)
64 return; // No protection active, handler should ignore.
65
66 // 2. Is it a signal we want to handle this way?
67 // Typically SIGBUS or SIGSEGV for memory access errors.
68 if (sig != SIGBUS && sig != SIGSEGV)
69 return; // Not a signal we are designed to recover from.
70
71 // 3. Did the fault occur within the registered protected range?
72 // Ensure siginfo_t is valid (it should be if SA_SIGINFO was used)
73 if (si == NULL)
74 return; // This shouldn't happen if sigaction was set up correctly with SA_SIGINFO
75
76 void *fault_addr = si->si_addr;
77
78 // Start from the most recent frame and work backwards
79 for (sig_atomic_t i = state->depth - 1; i >= 0; i--) {
80 protected_access_frame_t *frame = &state->stack[i];
81
82 // Skip inactive frames (shouldn't happen but check anyway)
83 if (frame->is_active != 1)
84 continue;
85
86 void *start_addr = frame->protected_start_addr;
87
88 // Check if start_addr is valid
89 if (start_addr == NULL)
90 continue;
91
92 // Calculate end address (exclusive)
93 void *end_addr = (unsigned char *)start_addr + frame->protected_size;
94
95 if (fault_addr >= start_addr && fault_addr < end_addr) {
96 // --- Conditions met! Perform recovery jump ---
97
98 // Mark that recovery jump is occurring *before* jumping.
99 // Set frame to '2'. This prevents handler re-entry if another signal occurs
100 // immediately, and signals to start() that recovery happened.
101 frame->is_active = 2;
102
103 // Store diagnostic information about the fault
104 frame->fault_address = fault_addr;
105 frame->signal_code = signal_code(sig, si->si_code);
106
107 // Update the depth to unwind all nested frames up to this one
108 state->depth = i;
109
110 // Jump back to the sigsetjmp point in PROTECTED_ACCESS_START
111 // The '1' becomes the non-zero return value of sigsetjmp.
112 siglongjmp(frame->jump_buffer, 1);
113
114 // --- Execution should not reach here after siglongjmp ---
115 // If it somehow did, something is fundamentally broken.
116 fprintf(stderr, "FATAL: siglongjmp returned in signal handler!\n");
117 abort();
118 return; // Should be unreachable
119 }
120 }
121
122 // Signal occurred while active, but fault address was outside all protected ranges.
123 // Let the default handler deal with it.
124 }