1 /** 2 * Handle page protection errors using D errors (exceptions) or asserts. 3 * 4 * License: Distributed under the 5 * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). 6 * (See accompanying file LICENSE_1_0.txt) 7 * Authors: Amaury SECHET, FeepingCreature, Vladimir Panteleev 8 * Source: $(DRUNTIMESRC etc/linux/memory.d) 9 */ 10 11 module etc.linux.memoryerror; 12 13 version (linux) 14 { 15 version (DigitalMars) 16 { 17 version (CRuntime_Glibc) 18 { 19 version (X86) 20 version = MemoryErrorSupported; 21 else version (X86_64) 22 version = MemoryErrorSupported; 23 } 24 } 25 } 26 27 version (linux) 28 { 29 version (X86) 30 version = MemoryAssertSupported; 31 else version (X86_64) 32 version = MemoryAssertSupported; 33 else version (ARM) 34 version = MemoryAssertSupported; 35 else version (AArch64) 36 version = MemoryAssertSupported; 37 else version (PPC64) 38 version = MemoryAssertSupported; 39 } 40 41 version (MemoryErrorSupported) 42 version = AnySupported; 43 else version (MemoryErrorSupported) 44 version = AnySupported; 45 46 version (AnySupported): 47 48 import core.sys.posix.signal; 49 import ucontext = core.sys.posix.ucontext; 50 51 version (MemoryAssertSupported) 52 { 53 import core.sys.posix.signal : SA_ONSTACK, sigaltstack, SIGSTKSZ, stack_t; 54 } 55 56 @system: 57 58 // The first 64Kb are reserved for detecting null pointer dereferences. 59 // TODO: this is a platform-specific assumption, can be made more robust 60 private enum size_t MEMORY_RESERVED_FOR_NULL_DEREFERENCE = 4096 * 16; 61 62 version (MemoryErrorSupported) 63 { 64 /** 65 * Register memory error handler, store the old handler. 66 * 67 * `NullPointerError` is thrown when dereferencing null pointers. 68 * A generic `InvalidPointerError` error is thrown in other cases. 69 * 70 * Returns: whether the registration was successful 71 * 72 * Limitations: Only x86 and x86_64 are supported for now. 73 */ 74 bool registerMemoryErrorHandler() nothrow 75 { 76 sigaction_t action; 77 action.sa_sigaction = &handleSignal; 78 action.sa_flags = SA_SIGINFO; 79 80 auto oldptr = &oldSigactionMemoryError; 81 82 return !sigaction(SIGSEGV, &action, oldptr); 83 } 84 85 /** 86 * Revert the memory error handler back to the one from before calling `registerMemoryErrorHandler()`. 87 * 88 * Returns: whether the registration of the old handler was successful 89 */ 90 bool deregisterMemoryErrorHandler() nothrow 91 { 92 auto oldptr = &oldSigactionMemoryError; 93 94 return !sigaction(SIGSEGV, oldptr, null); 95 } 96 97 /** 98 * Thrown on POSIX systems when a SIGSEGV signal is received. 99 */ 100 class InvalidPointerError : Error 101 { 102 this(string file = __FILE__, size_t line = __LINE__, Throwable next = null) nothrow 103 { 104 super("", file, line, next); 105 } 106 107 this(Throwable next, string file = __FILE__, size_t line = __LINE__) nothrow 108 { 109 super("", file, line, next); 110 } 111 } 112 113 /** 114 * Thrown on null pointer dereferences. 115 */ 116 class NullPointerError : InvalidPointerError 117 { 118 this(string file = __FILE__, size_t line = __LINE__, Throwable next = null) nothrow 119 { 120 super(file, line, next); 121 } 122 123 this(Throwable next, string file = __FILE__, size_t line = __LINE__) nothrow 124 { 125 super(file, line, next); 126 } 127 } 128 129 unittest 130 { 131 int* getNull() { return null; } 132 133 assert(registerMemoryErrorHandler()); 134 135 bool b; 136 137 try 138 { 139 *getNull() = 42; 140 } 141 catch (NullPointerError) 142 { 143 b = true; 144 } 145 146 assert(b); 147 148 b = false; 149 150 try 151 { 152 *getNull() = 42; 153 } 154 catch (InvalidPointerError) 155 { 156 b = true; 157 } 158 159 assert(b); 160 161 assert(deregisterMemoryErrorHandler()); 162 } 163 164 // Signal handler space. 165 166 private: 167 168 __gshared sigaction_t oldSigactionMemoryError; 169 170 alias RegType = typeof(ucontext.ucontext_t.init.uc_mcontext.gregs[0]); 171 172 version (X86_64) 173 { 174 static RegType savedRDI, savedRSI; 175 176 extern(C) 177 void handleSignal(int signum, siginfo_t* info, void* contextPtr) nothrow 178 { 179 auto context = cast(ucontext.ucontext_t*)contextPtr; 180 181 // Save registers into global thread local, to allow recovery. 182 savedRDI = context.uc_mcontext.gregs[ucontext.REG_RDI]; 183 savedRSI = context.uc_mcontext.gregs[ucontext.REG_RSI]; 184 185 // Hijack current context so we call our handler. 186 auto rip = context.uc_mcontext.gregs[ucontext.REG_RIP]; 187 auto addr = cast(RegType) info.si_addr; 188 context.uc_mcontext.gregs[ucontext.REG_RDI] = addr; 189 context.uc_mcontext.gregs[ucontext.REG_RSI] = rip; 190 context.uc_mcontext.gregs[ucontext.REG_RIP] = cast(RegType) ((rip != addr)?&sigsegvDataHandler:&sigsegvCodeHandler); 191 } 192 193 // All handler functions must be called with faulting address in RDI and original RIP in RSI. 194 195 // This function is called when the segfault's cause is to call an invalid function pointer. 196 void sigsegvCodeHandler() 197 { 198 asm 199 { 200 naked; 201 202 // Handle the stack for an invalid function call (segfault at RIP). 203 // With the return pointer, the stack is now alligned. 204 push RBP; 205 mov RBP, RSP; 206 207 jmp sigsegvDataHandler; 208 } 209 } 210 211 void sigsegvDataHandler() 212 { 213 asm 214 { 215 naked; 216 217 push RSI; // return address (original RIP). 218 push RBP; // old RBP 219 mov RBP, RSP; 220 221 pushfq; // Save flags. 222 push RAX; // RAX, RCX, RDX, and R8 to R11 are trash registers and must be preserved as local variables. 223 push RCX; 224 push RDX; 225 push R8; 226 push R9; 227 push R10; 228 push R11; // With 10 pushes, the stack is still aligned. 229 230 // Parameter address is already set as RAX. 231 call sigsegvUserspaceProcess; 232 233 // Restore RDI and RSI values. 234 call restoreRDI; 235 push RAX; // RDI is in RAX. It is pushed and will be poped back to RDI. 236 237 call restoreRSI; 238 mov RSI, RAX; 239 240 pop RDI; 241 242 // Restore trash registers value. 243 pop R11; 244 pop R10; 245 pop R9; 246 pop R8; 247 pop RDX; 248 pop RCX; 249 pop RAX; 250 popfq; // Restore flags. 251 252 // Return 253 pop RBP; 254 ret; 255 } 256 } 257 258 // The return value is stored in EAX and EDX, so this function restore the correct value for theses registers. 259 RegType restoreRDI() 260 { 261 return savedRDI; 262 } 263 264 RegType restoreRSI() 265 { 266 return savedRSI; 267 } 268 } 269 else version (X86) 270 { 271 static RegType savedEAX, savedEDX; 272 273 extern(C) 274 void handleSignal(int signum, siginfo_t* info, void* contextPtr) nothrow 275 { 276 auto context = cast(ucontext.ucontext_t*)contextPtr; 277 278 // Save registers into global thread local, to allow recovery. 279 savedEAX = context.uc_mcontext.gregs[ucontext.REG_EAX]; 280 savedEDX = context.uc_mcontext.gregs[ucontext.REG_EDX]; 281 282 // Hijack current context so we call our handler. 283 auto eip = context.uc_mcontext.gregs[ucontext.REG_EIP]; 284 auto addr = cast(RegType) info.si_addr; 285 context.uc_mcontext.gregs[ucontext.REG_EAX] = addr; 286 context.uc_mcontext.gregs[ucontext.REG_EDX] = eip; 287 context.uc_mcontext.gregs[ucontext.REG_EIP] = cast(RegType) ((eip != addr)?&sigsegvDataHandler:&sigsegvCodeHandler); 288 } 289 290 // All handler functions must be called with faulting address in EAX and original EIP in EDX. 291 292 // This function is called when the segfault's cause is to call an invalid function pointer. 293 void sigsegvCodeHandler() 294 { 295 asm 296 { 297 naked; 298 299 // Handle the stack for an invalid function call (segfault at EIP). 300 // 4 bytes are used for function pointer; We need 12 byte to keep stack aligned. 301 sub ESP, 12; 302 mov [ESP + 8], EBP; 303 mov EBP, ESP; 304 305 jmp sigsegvDataHandler; 306 } 307 } 308 309 void sigsegvDataHandler() 310 { 311 asm 312 { 313 naked; 314 315 // We jump directly here if we are in a valid function call case. 316 push EDX; // return address (original EIP). 317 push EBP; // old EBP 318 mov EBP, ESP; 319 320 pushfd; // Save flags. 321 push ECX; // ECX is a trash register and must be preserved as local variable. 322 // 4 pushes have been done. The stack is aligned. 323 324 // Parameter address is already set as EAX. 325 call sigsegvUserspaceProcess; 326 327 // Restore register values and return. 328 call restoreRegisters; 329 330 pop ECX; 331 popfd; // Restore flags. 332 333 // Return 334 pop EBP; 335 ret; 336 } 337 } 338 339 // The return value is stored in EAX and EDX, so this function restore the correct value for theses registers. 340 RegType[2] restoreRegisters() 341 { 342 RegType[2] restore; 343 restore[0] = savedEAX; 344 restore[1] = savedEDX; 345 346 return restore; 347 } 348 } 349 else 350 { 351 static assert(false, "Unsupported architecture."); 352 } 353 354 // User space handler 355 void sigsegvUserspaceProcess(void* address) 356 { 357 // SEGV_MAPERR, SEGV_ACCERR. 358 // The first page is protected to detect null dereferences. 359 if ((cast(size_t) address) < MEMORY_RESERVED_FOR_NULL_DEREFERENCE) 360 { 361 throw new NullPointerError(); 362 } 363 364 throw new InvalidPointerError(); 365 } 366 } 367 368 version (MemoryAssertSupported) 369 { 370 private __gshared sigaction_t oldSigactionMemoryAssert; // sigaction before calling `registerMemoryAssertHandler` 371 372 /** 373 * Registers a signal handler for SIGSEGV that turns them into an assertion failure, 374 * providing a more descriptive error message and stack trace if the program is 375 * compiled with debug info and D assertions (as opposed to C assertions). 376 * 377 * Differences with the `registerMemoryErrorHandler` version are: 378 * - The handler is registered with SA_ONSTACK, so it can handle stack overflows. 379 * - It uses `assert(0)` instead of `throw new Error` and doesn't support catching the error. 380 * - This is a template so that the -check and -checkaction flags of the compiled program are used, 381 * instead of the ones used for compiling druntime. 382 * 383 * Returns: whether the registration was successful 384 */ 385 bool registerMemoryAssertHandler()() 386 { 387 nothrow @nogc extern(C) 388 void _d_handleSignalAssert(int signum, siginfo_t* info, void* contextPtr) 389 { 390 // Guess the reason for the segfault by seeing if the faulting address 391 // is close to the stack pointer or the null pointer. 392 393 const void* segfaultingPtr = info.si_addr; 394 395 auto context = cast(ucontext.ucontext_t*) contextPtr; 396 version (X86_64) 397 const stackPtr = cast(void*) context.uc_mcontext.gregs[ucontext.REG_RSP]; 398 else version (X86) 399 const stackPtr = cast(void*) context.uc_mcontext.gregs[ucontext.REG_ESP]; 400 else version (ARM) 401 const stackPtr = cast(void*) context.uc_mcontext.arm_sp; 402 else version (AArch64) 403 const stackPtr = cast(void*) context.uc_mcontext.sp; 404 else version (PPC64) 405 const stackPtr = cast(void*) context.uc_mcontext.regs.gpr[1]; 406 else 407 static assert(false, "Unsupported architecture."); // TODO: other architectures 408 auto distanceToStack = cast(ptrdiff_t) (stackPtr - segfaultingPtr); 409 if (distanceToStack < 0) 410 distanceToStack = -distanceToStack; 411 412 if (stackPtr && distanceToStack <= 4096) 413 assert(false, "segmentation fault: call stack overflow"); 414 else if (cast(size_t) segfaultingPtr < MEMORY_RESERVED_FOR_NULL_DEREFERENCE) 415 assert(false, "segmentation fault: null pointer read/write operation"); 416 else 417 assert(false, "segmentation fault: invalid pointer read/write operation"); 418 } 419 420 sigaction_t action; 421 action.sa_sigaction = &_d_handleSignalAssert; 422 action.sa_flags = SA_SIGINFO | SA_ONSTACK; 423 424 // Set up alternate stack, because segfaults can be caused by stack overflow, 425 // in which case the stack is already exhausted 426 __gshared ubyte[SIGSTKSZ] altStack; 427 stack_t ss; 428 ss.ss_sp = altStack.ptr; 429 ss.ss_size = altStack.length; 430 ss.ss_flags = 0; 431 if (sigaltstack(&ss, null) == -1) 432 return false; 433 434 return !sigaction(SIGSEGV, &action, &oldSigactionMemoryAssert); 435 } 436 437 /** 438 * Revert the memory error handler back to the one from before calling `registerMemoryAssertHandler()`. 439 * 440 * Returns: whether the registration of the old handler was successful 441 */ 442 bool deregisterMemoryAssertHandler() 443 { 444 return !sigaction(SIGSEGV, &oldSigactionMemoryAssert, null); 445 } 446 447 unittest 448 { 449 // Testing actual memory errors is done in the test suite 450 assert(registerMemoryAssertHandler()); 451 assert(deregisterMemoryAssertHandler()); 452 } 453 }