11#include < echion/danger.h>
2-
3- #include < cstdio>
2+ #include < echion/state.h>
43
54#include < algorithm>
5+ #include < cassert>
66#include < cerrno>
7-
8- #include < echion/state.h>
7+ #include < csetjmp>
8+ #include < cstdio>
9+ #include < sys/mman.h>
10+ #include < signal.h>
11+ #include < string.h>
12+ #include < unistd.h>
913
1014struct sigaction g_old_segv;
1115struct sigaction g_old_bus;
1216
1317thread_local ThreadAltStack t_altstack;
1418
15- thread_local volatile sig_atomic_t t_faulted;
16- thread_local volatile void * t_landing_ip;
19+ thread_local volatile sig_atomic_t t_faulted = 0 ;
1720
18- void arm_landing (void * p) {
19- t_landing_ip = p;
21+ // We "arm" by publishing a valid jmp env for this thread.
22+ thread_local sigjmp_buf t_jmpenv;
23+ thread_local volatile sig_atomic_t t_jmpenv_armed = 0 ;
24+
25+ static inline void arm_landing () {
26+ t_jmpenv_armed = 1 ;
2027 __asm__ __volatile__ (" " ::: " memory" );
2128}
2229
23- void disarm_landing () {
30+ static inline void disarm_landing () {
2431 __asm__ __volatile__ (" " ::: " memory" );
25- t_landing_ip = nullptr ;
32+ t_jmpenv_armed = 0 ;
2633}
2734
28- void segv_handler (int signo, siginfo_t *, void * uctx) {
29- if (!t_landing_ip) {
30- struct sigaction *old = (signo == SIGSEGV ) ? &g_old_segv : &g_old_bus;
31-
32- // Restore the previous handler and re-raise
33- sigaction (signo, old, NULL );
35+ static void segv_handler (int signo, siginfo_t *, void *) {
36+ if (!t_jmpenv_armed) {
37+ struct sigaction * old = (signo == SIGSEGV ) ? &g_old_segv : &g_old_bus;
38+ // Restore the previous handler and re-raise so default/old handling occurs.
39+ sigaction (signo, old, nullptr );
3440 raise (signo);
41+ return ;
3542 }
3643
3744 t_faulted = 1 ;
3845
39- ucontext_t * uc = reinterpret_cast <ucontext_t *>(uctx);
40- #if defined(__APPLE__)
41-
42- // macOS
43- # if defined(__x86_64__)
44- // RIP is in the saved general-purpose register set
45- ((ucontext_t *)uc)->uc_mcontext ->__ss .__rip = (uint64_t )(uintptr_t )t_landing_ip;
46- # elif defined(__i386__)
47- ((ucontext_t *)uc)->uc_mcontext ->__ss .__eip = (uint32_t )(uintptr_t )t_landing_ip;
48- # elif defined(__aarch64__)
49- ((ucontext_t *)uc)->uc_mcontext ->__ss .__pc = (uint64_t )(uintptr_t )t_landing_ip;
50- # else
51- # error "Unsupported macOS architecture"
52- # endif
53-
54- #else
55-
56- // Linux / other libcs
57- # if defined(__x86_64__) && defined(REG_RIP)
58- ((ucontext_t *)uc)->uc_mcontext .gregs [REG_RIP ] = (greg_t )(uintptr_t )t_landing_ip;
59- # elif defined(__i386__) && defined(REG_EIP)
60- ((ucontext_t *)uc)->uc_mcontext .gregs [REG_EIP ] = (greg_t )(uintptr_t )t_landing_ip;
61- # elif defined(__aarch64__)
62- ((ucontext_t *)uc)->uc_mcontext .pc = (uintptr_t )t_landing_ip; // or gregs[REG_PC] depending on libc
63- # else
64- # error "Set instruction pointer not implemented for this architecture/libc"
65- # endif
66-
67- #endif
46+ // Jump back to the armed site. Use 1 so sigsetjmp returns nonzero.
47+ siglongjmp (t_jmpenv, 1 );
6848}
6949
70- void ensure_altstack_for_this_thread () {
50+ static inline void ensure_altstack_for_this_thread () {
7151 t_altstack.ensure_installed ();
7252}
7353
7454int init_segv_catcher () {
7555 ensure_altstack_for_this_thread ();
7656
77- // 1) Alternate signal stack so handler runs even if the thread's normal stack is bad.
78- const size_t sz = 1 << 20 ; // 1 MiB is plenty for a tiny handler
79-
80- void * mem = mmap (NULL , sz, PROT_READ | PROT_WRITE , MAP_PRIVATE | MAP_ANONYMOUS , -1 , 0 );
57+ // Reserve a page of memory just like before (not strictly necessary here,
58+ // but kept to preserve behavior/footprint).
59+ const size_t sz = 1 << 20 ;
60+ void * mem = mmap (nullptr , sz, PROT_READ | PROT_WRITE ,
61+ MAP_PRIVATE | MAP_ANONYMOUS , -1 , 0 );
8162 if (mem == MAP_FAILED ) {
8263 return -1 ;
8364 }
8465
85- // 2) Install handler (SA_SIGINFO for the ucontext if you ever need it; SA_ONSTACK to use altstack)
8666 struct sigaction sa{};
8767 sa.sa_sigaction = segv_handler;
8868 sigemptyset (&sa.sa_mask );
69+ // SA_SIGINFO for 3-arg handler; SA_ONSTACK to run on alt stack.
8970 sa.sa_flags = SA_SIGINFO | SA_ONSTACK ;
9071 if (sigaction (SIGSEGV , &sa, &g_old_segv) != 0 ) {
9172 return -1 ;
9273 }
93- if (sigaction (SIGBUS , &sa, &g_old_bus) != 0 ) {
74+ if (sigaction (SIGBUS , &sa, &g_old_bus) != 0 ) {
75+ // Try to roll back SIGSEGV install on failure.
76+ sigaction (SIGSEGV , &g_old_segv, nullptr );
9477 return -1 ;
9578 }
9679
@@ -108,26 +91,34 @@ safe_memcpy_return_t safe_memcpy(void* dst, const void* src, size_t n) {
10891
10992 t_faulted = 0 ;
11093
111- uint8_t * d = static_cast <uint8_t *>(dst);
112- const uint8_t * s = static_cast <const uint8_t *>(src);
113- safe_memcpy_return_t rem = n ;
94+ auto * d = static_cast <uint8_t *>(dst);
95+ auto * s = static_cast <const uint8_t *>(src);
96+ safe_memcpy_return_t rem = static_cast < safe_memcpy_return_t >(n) ;
11497
115- // Publish landing target for the handler
116- arm_landing (&&landing);
98+ // Arm and capture a landing site. Save/restore signal mask with savesigs=1.
99+ arm_landing ();
100+ if (sigsetjmp (t_jmpenv, /* savesigs=*/ 1 ) != 0 ) {
101+ // We arrived here from siglongjmp after a fault.
102+ goto landing;
103+ }
117104
118- // Copy in page-bounded chunks (at most one fault per bad page)
105+ // Copy in page-bounded chunks (at most one fault per bad page).
119106 while (rem && !t_faulted) {
120- safe_memcpy_return_t to_src_pg = 4096 - (reinterpret_cast <uintptr_t >(s) & 4095 );
121- safe_memcpy_return_t to_dst_pg = 4096 - (reinterpret_cast <uintptr_t >(d) & 4095 );
107+ safe_memcpy_return_t to_src_pg =
108+ 4096 - (static_cast <uintptr_t >(reinterpret_cast <uintptr_t >(s)) & 4095 );
109+ safe_memcpy_return_t to_dst_pg =
110+ 4096 - (static_cast <uintptr_t >(reinterpret_cast <uintptr_t >(d)) & 4095 );
122111 safe_memcpy_return_t chunk = std::min (rem, std::min (to_src_pg, to_dst_pg));
123112
124113 // Optional early probe to fault before entering large memcpy
125114 (void )*reinterpret_cast <volatile const uint8_t *>(s);
126115
127- // If this faults, we'll resume at `landing` label
128- (void )memcpy (d, s, chunk);
116+ // If this faults, we'll siglongjmp back to the sigsetjmp above.
117+ (void )memcpy (d, s, static_cast < size_t >( chunk) );
129118
130- d += chunk; s += chunk; rem -= chunk;
119+ d += chunk;
120+ s += chunk;
121+ rem -= chunk;
131122 }
132123
133124landing:
@@ -138,38 +129,38 @@ safe_memcpy_return_t safe_memcpy(void* dst, const void* src, size_t n) {
138129 return -1 ;
139130 }
140131
141- return n ;
132+ return static_cast < safe_memcpy_return_t >(n) ;
142133}
143134
144135#if defined PL_LINUX
145136ssize_t safe_memcpy_wrapper (
146137 pid_t ,
147- const struct iovec * __dstvec,
138+ const struct iovec * __dstvec,
148139 unsigned long int __dstiovcnt,
149- const struct iovec * __srcvec,
140+ const struct iovec * __srcvec,
150141 unsigned long int __srciovcnt,
151- unsigned long int )
152- {
142+ unsigned long int ) {
153143 (void )__dstiovcnt;
154144 (void )__srciovcnt;
155145 assert (__dstiovcnt == 1 );
156146 assert (__srciovcnt == 1 );
157147
158- return safe_memcpy (__dstvec->iov_base , __srcvec->iov_base , std::min (__dstvec->iov_len , __srcvec->iov_len ));
148+ size_t to_copy = std::min (__dstvec->iov_len , __srcvec->iov_len );
149+ return safe_memcpy (__dstvec->iov_base , __srcvec->iov_base , to_copy);
159150}
160151#elif defined PL_DARWIN
161- kern_return_t safe_memcpy_wrapper
162- (
163- vm_map_read_t target_task,
164- mach_vm_address_t address,
165- mach_vm_size_t size,
166- mach_vm_address_t data,
167- mach_vm_size_t *outsize
168- ) {
152+ kern_return_t safe_memcpy_wrapper (
153+ vm_map_read_t target_task,
154+ mach_vm_address_t address,
155+ mach_vm_size_t size,
156+ mach_vm_address_t data,
157+ mach_vm_size_t * outsize) {
169158 (void )target_task;
170159
171- auto copied = safe_memcpy (reinterpret_cast <void *>(address), reinterpret_cast <void *>(data), size);
160+ auto copied = safe_memcpy (reinterpret_cast <void *>(address),
161+ reinterpret_cast <void *>(data),
162+ static_cast <size_t >(size));
172163 *outsize = copied;
173164 return copied == size ? KERN_SUCCESS : KERN_FAILURE ;
174165}
175- #endif
166+ #endif
0 commit comments