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 ---
59
60
protected_access_t *state = &protected_access_state;
61
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.
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.
74
return; // This shouldn't happen if sigaction was set up correctly with SA_SIGINFO
75
76
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
- }
77
49
- // Calculate end address (exclusive)
50
- void *end_addr = (unsigned char *)start_addr + state->protected_size;
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
52
- if (fault_addr >= start_addr && fault_addr < end_addr) {
53
- // --- Conditions met! Perform recovery jump ---
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
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;
107
+ // Update the depth to unwind all nested frames up to this one
108
+ state->depth = i;
109
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);
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
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
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
71
- // Signal occurred while active, but fault address was outside the protected range.
122
+ // Signal occurred while active, but fault address was outside all protected ranges.
123
// Let the default handler deal with it.
124
}