master
h 108 lines 6.57 KB
Raw
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 // Maximum nesting depth for protected access regions
10 #define PROTECTED_ACCESS_MAX_NESTING 8
11
12 typedef struct {
13 const char *caller; // Function that initiated the protected region
14 sigjmp_buf jump_buffer; // Where to jump back to
15 void *protected_start_addr; // Start of the monitored memory range
16 size_t protected_size; // Size of the monitored memory range
17 // 0=inactive, 1=active (in protected block), 2=jump occurred
18 volatile sig_atomic_t is_active; // Must be sig_atomic_t for signal handler safety
19
20 // Enhanced diagnostic information
21 const char *resource_name; // Name/path of the resource being accessed (often a file path)
22 const char *operation; // Operation being performed (e.g. "read", "write", "mmap")
23 SIGNAL_CODE signal_code; // Signal and code combination (e.g. SIGSEGV/SEGV_MAPERR)
24 void *fault_address; // The exact address that caused the fault
25 } protected_access_frame_t;
26
27 typedef struct {
28 protected_access_frame_t stack[PROTECTED_ACCESS_MAX_NESTING];
29 volatile sig_atomic_t depth; // Current nesting depth (0 = no active protection)
30 } protected_access_t;
31
32 extern __thread protected_access_t protected_access_state;
33
34 #define PROTECTED_ACCESS_START(start, size, resource, op) ({ \
35 bool _rc = false; \
36 \
37 if (protected_access_state.depth >= PROTECTED_ACCESS_MAX_NESTING) \
38 fatal("PROTECTED ACCESS: maximum nesting depth reached in function %s", \
39 __FUNCTION__); \
40 \
41 if (start && size) { \
42 /* Get the current frame on the stack */ \
43 protected_access_frame_t *frame = \
44 &protected_access_state.stack[protected_access_state.depth]; \
45 \
46 /* Initialize the frame */ \
47 frame->protected_start_addr = start; \
48 frame->protected_size = size; \
49 frame->is_active = 1; \
50 frame->caller = __FUNCTION__; \
51 frame->resource_name = resource; \
52 frame->operation = op; \
53 frame->signal_code = 0; \
54 frame->fault_address = NULL; \
55 \
56 /* Increase the stack depth before setting up the jump */ \
57 protected_access_state.depth++; \
58 \
59 if (sigsetjmp(frame->jump_buffer, 1) == 0) { \
60 /* Initial call successful, sigsetjmp returns 0. */ \
61 _rc = true; \
62 } else { \
63 /* Returned here via siglongjmp from the signal handler. */ \
64 /* The handler should have set frame->is_active = 2 */ \
65 /* and populated diagnostic information. */ \
66 /* Return false to indicate recovery path should be taken. */ \
67 _rc = false; \
68 } \
69 } \
70 _rc; \
71 })
72
73 static inline void protected_access_end(volatile int *ptr __maybe_unused) {
74 if (protected_access_state.depth > 0) {
75 /* Decrease the stack depth */
76 protected_access_state.depth--;
77
78 /* Clear the frame at the current depth */
79 protected_access_frame_t *frame = &protected_access_state.stack[protected_access_state.depth];
80 frame->is_active = 0;
81 frame->protected_start_addr = NULL;
82 frame->protected_size = 0;
83 /* No need to clear jump_buffer explicitly */
84 }
85 }
86
87 #define PROTECTED_ACCESS_AUTO_CLEANUP() \
88 volatile int _pa_dummy_cleanup_var __attribute__((cleanup(protected_access_end), unused)) = 0;
89
90 #define PROTECTED_ACCESS_END() protected_access_end(NULL);
91
92 #define PROTECTED_ACCESS_SETUP(start, size, resource, op) \
93 PROTECTED_ACCESS_AUTO_CLEANUP(); \
94 bool no_signal_received = PROTECTED_ACCESS_START(start, size, resource, op); \
95 if (!no_signal_received) { \
96 char __pa_error_buf[1024]; \
97 protected_access_format_error(__pa_error_buf, sizeof(__pa_error_buf)); \
98 nd_log_limit_static_thread_var(_erl, 10, 0); \
99 nd_log_limit(&_erl, NDLS_DAEMON, NDLP_ERR, "%s", __pa_error_buf); \
100 }
101
102 void signal_protected_access_check(int sig, siginfo_t *si, void *context);
103
104 // Function declarations for diagnostic functions
105 const protected_access_frame_t *protected_access_get_last_fault(void);
106 void protected_access_format_error(char *buffer, size_t buffer_size);
107
108 #endif // PROTECTED_ACCESS_H