Reactos
at master 582 lines 19 kB view raw
1// 2// signal.cpp 3// 4// Copyright (c) Microsoft Corporation. All rights reserved. 5// 6// signal(), raise(), and related functions 7// 8#include <corecrt_internal.h> 9#include <errno.h> 10#include <excpt.h> 11#include <float.h> 12#include <malloc.h> 13#include <signal.h> 14#include <stddef.h> 15#include <string.h> 16 17 18 19// Variables holding action codes (and code pointers) for SIGINT, SIGBRK, 20// SIGABRT and SIGTERM. 21// 22// note that the disposition (i.e., action to be taken upon receipt) of 23// these signals is defined on a per-process basis (not per-thread)!! 24static __crt_state_management::dual_state_global<__crt_signal_handler_t> ctrlc_action; // SIGINT 25static __crt_state_management::dual_state_global<__crt_signal_handler_t> ctrlbreak_action; // SIGBREAK 26static __crt_state_management::dual_state_global<__crt_signal_handler_t> abort_action; // SIGABRT 27static __crt_state_management::dual_state_global<__crt_signal_handler_t> term_action; // SIGTERM 28 29/* 30 * flag indicated whether or not a handler has been installed to capture 31 * ^C and ^Break events. 32 */ 33static bool console_ctrl_handler_installed = false; 34 35#define _SIGHUP_IGNORE 1 36#define _SIGQUIT_IGNORE 3 37#define _SIGPIPE_IGNORE 13 38#define _SIGIOINT_IGNORE 16 39#define _SIGSTOP_IGNORE 17 40 41 42 43// Initializes the signal handler pointers to the encoded nullptr value. 44extern "C" void __cdecl __acrt_initialize_signal_handlers(void* const encoded_nullptr) 45{ 46 // The encoded nullptr is SIG_DFL 47 ctrlc_action.initialize (reinterpret_cast<__crt_signal_handler_t>(encoded_nullptr)); 48 ctrlbreak_action.initialize(reinterpret_cast<__crt_signal_handler_t>(encoded_nullptr)); 49 abort_action.initialize (reinterpret_cast<__crt_signal_handler_t>(encoded_nullptr)); 50 term_action.initialize (reinterpret_cast<__crt_signal_handler_t>(encoded_nullptr)); 51} 52 53 54 55// Gets the address of the global action for one of the signals that has a 56// global action. Returns nullptr if the given signal does not have a global 57// action. 58static __crt_signal_handler_t* __cdecl get_global_action_nolock(int const signum) throw() 59{ 60 switch (signum) 61 { 62 // CRT_REFACTOR TODO: PERFORMANCE: for OS mode, these might be able to be in the const data section, instead of in writeable page data. 63 case SIGINT: return &ctrlc_action.value(); 64 case SIGBREAK: return &ctrlbreak_action.value(); 65 case SIGABRT: return &abort_action.value(); 66 case SIGABRT_COMPAT: return &abort_action.value(); 67 case SIGTERM: return &term_action.value(); 68 } 69 70 return nullptr; 71} 72 73 74 75// Looks up the exception-action table entry for a signal. This function finds 76// the first entry in the 'action_table' whose _signal_number field is 'signum'. 77// If no such entry is found, nullptr is returned. 78static __crt_signal_action_t* __cdecl siglookup( 79 int const signum, 80 __crt_signal_action_t* const action_table 81 ) throw() 82{ 83 // Walk through the table looking for the proper entry. Note that in the 84 // case where more than one exception corresponds to the same signal, the 85 // first such instance in the table is the one returned. 86 for (__crt_signal_action_t* p = action_table; p != action_table + __acrt_signal_action_table_count; ++p) 87 if (p->_signal_number == signum) 88 return p; 89 90 // If we reached the end of the table, return nullptr: 91 return nullptr; 92} 93 94 95 96// Handles failures for signal(). 97static __crt_signal_handler_t __cdecl signal_failed(int const signum) throw() 98{ 99 switch (signum) 100 { 101 case _SIGHUP_IGNORE: 102 case _SIGQUIT_IGNORE: 103 case _SIGPIPE_IGNORE: 104 case _SIGIOINT_IGNORE: 105 case _SIGSTOP_IGNORE: 106 return SIG_ERR; 107 108 default: 109 errno = EINVAL; 110 return SIG_ERR; 111 } 112} 113 114// Enclaves have no console, thus also no signals specific to consoles. 115#ifdef _UCRT_ENCLAVE_BUILD 116 117__inline static BOOL is_unsupported_signal(int const signum, __crt_signal_handler_t const sigact) 118{ 119 return (sigact == SIG_ACK || sigact == SIG_SGE || signum == SIGINT || signum == SIGBREAK); 120} 121 122__inline static BOOL is_console_signal(int const) 123{ 124 return FALSE; 125} 126 127#else /* ^^^ _UCRT_ENCLAVE_BUILD ^^^ // vvv !_UCRT_ENCLAVE_BUILD vvv */ 128 129__inline static BOOL is_unsupported_signal(int const, __crt_signal_handler_t const sigact) 130{ 131 return (sigact == SIG_ACK || sigact == SIG_SGE); 132} 133 134__inline static BOOL is_console_signal(int const signum) 135{ 136 return (signum == SIGINT || signum == SIGBREAK); 137} 138 139#endif /* _UCRT_ENCLAVE_BUILD */ 140 141/*** 142*static BOOL WINAPI ctrlevent_capture(DWORD ctrl_type) - capture ^C and ^Break events 143* 144*Purpose: 145* Capture ^C and ^Break events from the console and dispose of them 146* according the values in ctrlc_action and ctrlbreak_action, resp. 147* This is the routine that evokes the user-defined action for SIGINT 148* (^C) or SIGBREAK (^Break) installed by a call to signal(). 149* 150*Entry: 151* DWORD ctrl_type - indicates type of event, two values: 152* CTRL_C_EVENT 153* CTRL_BREAK_EVENT 154* 155*Exit: 156* Returns TRUE to indicate the event (signal) has been handled. 157* Otherwise, returns FALSE. 158* 159*Exceptions: 160* 161*******************************************************************************/ 162 163static BOOL WINAPI ctrlevent_capture(DWORD const ctrl_type) throw() 164{ 165 __crt_signal_handler_t ctrl_action = nullptr; 166 int signal_code = 0; 167 168 __acrt_lock(__acrt_signal_lock); 169 __try 170 { 171 __crt_signal_handler_t* pctrl_action; 172 173 // Identify the type of event and fetch the corresponding action 174 // description: 175 if (ctrl_type == CTRL_C_EVENT) 176 { 177 pctrl_action = &ctrlc_action.value(); 178 ctrl_action = __crt_fast_decode_pointer(*pctrl_action); 179 signal_code = SIGINT; 180 } 181 else 182 { 183 pctrl_action = &ctrlbreak_action.value(); 184 ctrl_action = __crt_fast_decode_pointer(*pctrl_action); 185 signal_code = SIGBREAK; 186 } 187 188 if (ctrl_action != SIG_DFL && ctrl_action != SIG_IGN) 189 { 190 // Reset the action to be SIG_DFL: 191 *pctrl_action = __crt_fast_encode_pointer(nullptr); 192 } 193 } 194 __finally 195 { 196 __acrt_unlock(__acrt_signal_lock); 197 } 198 __endtry 199 200 // The default signal action leaves the event unhandled, so return false to 201 // indicate such: 202 if (ctrl_action == SIG_DFL) 203 return FALSE; 204 205 // If the action is not to ignore the signal, then invoke the action: 206 if (ctrl_action != SIG_IGN) 207 (*ctrl_action)(signal_code); 208 209 // Then return TRUE to indicate the event has been handled (this may mean 210 // that the even is being ignored): 211 return TRUE; 212} 213 214 215/*** 216*__crt_signal_handler_t signal(signum, sigact) - Define a signal handler 217* 218*Purpose: 219* The signal routine allows the user to define what action should 220* be taken when various signals occur. The Win32/Dosx32 implementation 221* supports seven signals, divided up into three general groups 222* 223* 1. Signals corresponding to OS exceptions. These are: 224* SIGFPE 225* SIGILL 226* SIGSEGV 227* Signal actions for these signals are installed by altering the 228* _action and SigAction fields for the appropriate entry in the 229* exception-action table (XcptActTab[]). 230* 231* 2. Signals corresponding to ^C and ^Break. These are: 232* SIGINT 233* SIGBREAK 234* Signal actions for these signals are installed by altering the 235* _ctrlc_action and _ctrlbreak_action variables. 236* 237* 3. Signals which are implemented only in the runtime. That is, they 238* occur only as the result of a call to raise(). 239* SIGABRT 240* SIGTERM 241* 242* 243*Entry: 244* int signum signal type. recognized signal types are: 245* 246* SIGABRT (ANSI) 247* SIGBREAK 248* SIGFPE (ANSI) 249* SIGILL (ANSI) 250* SIGINT (ANSI) 251* SIGSEGV (ANSI) 252* SIGTERM (ANSI) 253* SIGABRT_COMPAT 254* 255* __crt_signal_handler_t sigact signal handling function or action code. the action 256* codes are: 257* 258* SIG_DFL - take the default action, whatever that may 259* be, upon receipt of this type type of signal. 260* 261* SIG_DIE - *** ILLEGAL *** 262* special code used in the _action field of an 263* XcptActTab[] entry to indicate that the runtime is 264* to terminate the process upon receipt of the exception. 265* not accepted as a value for sigact. 266* 267* SIG_IGN - ignore this type of signal 268* 269* [function address] - transfer control to this address 270* when a signal of this type occurs. 271* 272*Exit: 273* Good return: 274* Signal returns the previous value of the signal handling function 275* (e.g., SIG_DFL, SIG_IGN, etc., or [function address]). This value is 276* returned in DX:AX. 277* 278* Error return: 279* Signal returns -1 and errno is set to EINVAL. The error return is 280* generally taken if the user submits bogus input values. 281* 282*Exceptions: 283* None. 284* 285*******************************************************************************/ 286 287extern "C" __crt_signal_handler_t __cdecl signal(int signum, __crt_signal_handler_t sigact) 288{ 289 // Check for signal actions that are supported on other platforms but not on 290 // this one, and make sure the action is not SIG_DIE: 291 if (is_unsupported_signal(signum, sigact)) 292 return signal_failed(signum); 293 294 // First, handle the case where the signal does not correspond to an 295 // exception in the host OS: 296 if (signum == SIGINT || 297 signum == SIGBREAK || 298 signum == SIGABRT || 299 signum == SIGABRT_COMPAT || 300 signum == SIGTERM) 301 { 302 bool set_console_ctrl_error = false; 303 __crt_signal_handler_t old_action = nullptr; 304 305 __acrt_lock(__acrt_signal_lock); 306 __try 307 { 308 // If the signal is SIGINT or SIGBREAK make sure the handler is 309 // installed to capture ^C and ^Break events: 310 // C4127: conditional expression is constant 311#pragma warning( suppress: 4127 ) 312 if (is_console_signal(signum) && !console_ctrl_handler_installed) 313 { 314 if (SetConsoleCtrlHandler(ctrlevent_capture, TRUE)) 315 { 316 console_ctrl_handler_installed = true; 317 } 318 else 319 { 320 _doserrno = GetLastError(); 321 set_console_ctrl_error = true; 322 } 323 } 324 325 __crt_signal_handler_t* const action_pointer = get_global_action_nolock(signum); 326 if (action_pointer != nullptr) 327 { 328 old_action = __crt_fast_decode_pointer(*action_pointer); 329 if (sigact != SIG_GET) 330 *action_pointer = __crt_fast_encode_pointer(sigact); 331 } 332 } 333 __finally 334 { 335 __acrt_unlock(__acrt_signal_lock); 336 } 337 __endtry 338 339 if (set_console_ctrl_error) 340 return signal_failed(signum); 341 342 return old_action; 343 } 344 345 346 // If we reach here, signum is supposed to be one of the signals which 347 // correspond to exceptions on the host OS. If it's not one of these, 348 // fail and return immediately: 349 if (signum != SIGFPE && signum != SIGILL && signum != SIGSEGV) 350 return signal_failed(signum); 351 352 __acrt_ptd* const ptd = __acrt_getptd_noexit(); 353 if (ptd == nullptr) 354 return signal_failed(signum); 355 356 // Check that there is a per-thread instance of the exception-action table 357 // for this thread. If there isn't, create one: 358 if (ptd->_pxcptacttab == __acrt_exception_action_table) 359 { 360 // Allocate space for an exception-action table: 361 ptd->_pxcptacttab = static_cast<__crt_signal_action_t*>(_malloc_crt(__acrt_signal_action_table_size)); 362 if (ptd->_pxcptacttab == nullptr) 363 return signal_failed(signum); 364 365 // Initialize the table by copying the contents of __acrt_exception_action_table: 366 memcpy(ptd->_pxcptacttab, __acrt_exception_action_table, __acrt_signal_action_table_size); 367 } 368 369 // Look up the proper entry in the exception-action table. Note that if 370 // several exceptions are mapped to the same signal, this returns the 371 // pointer to first such entry in the exception action table. It is assumed 372 // that the other entries immediately follow this one. 373 __crt_signal_action_t* const xcpt_action = siglookup(signum, ptd->_pxcptacttab); 374 if (xcpt_action == nullptr) 375 return signal_failed(signum); 376 377 // SIGSEGV, SIGILL and SIGFPE all have more than one exception mapped to 378 // them. The code below depends on the exceptions corresponding to the same 379 // signal being grouped together in the exception-action table. 380 381 __crt_signal_handler_t const old_action = xcpt_action->_action; 382 383 // If we are not just getting the currently installed action, loop through 384 // all the entries corresponding to the given signal and update them as 385 // appropriate: 386 if (sigact != SIG_GET) 387 { 388 __crt_signal_action_t* const last = ptd->_pxcptacttab + __acrt_signal_action_table_count; 389 390 // Iterate until we reach the end of the table or we reach the end of 391 // the range of actions for this signal, whichever comes first: 392 for (__crt_signal_action_t* p = xcpt_action; p != last && p->_signal_number == signum; ++p) 393 { 394 p->_action = sigact; 395 } 396 } 397 398 return old_action; 399} 400 401 402 403/*** 404*int raise(signum) - Raise a signal 405* 406*Purpose: 407* This routine raises a signal (i.e., performs the action currently 408* defined for this signal). The action associated with the signal is 409* evoked directly without going through intermediate dispatching or 410* handling. 411* 412*Entry: 413* int signum - signal type (e.g., SIGINT) 414* 415*Exit: 416* returns 0 on good return, -1 on bad return. 417* 418*Exceptions: 419* May not return. Raise has no control over the action 420* routines defined for the various signals. Those routines may 421* abort, terminate, etc. In particular, the default actions for 422* certain signals will terminate the program. 423* 424*******************************************************************************/ 425extern "C" int __cdecl raise(int const signum) 426{ 427 __acrt_ptd* ptd = nullptr; 428 int old_fpecode = 0; 429 430 __crt_signal_handler_t* action_pointer = nullptr; 431 bool action_is_global = true; 432 switch (signum) 433 { 434 case SIGINT: 435 case SIGBREAK: 436 case SIGABRT: 437 case SIGABRT_COMPAT: 438 case SIGTERM: 439 action_pointer = get_global_action_nolock(signum);; 440 break; 441 442 case SIGFPE: 443 case SIGILL: 444 case SIGSEGV: 445 { 446 ptd = __acrt_getptd_noexit(); 447 if (ptd == nullptr) 448 return -1; 449 450 __crt_signal_action_t* const local_action = siglookup(signum, ptd->_pxcptacttab); 451 _VALIDATE_RETURN(local_action != nullptr, EINVAL, -1); 452 action_pointer = &local_action->_action; 453 action_is_global = false; 454 break; 455 } 456 default: 457 // unsupported signal, return an error 458 _VALIDATE_RETURN(("Invalid signal or error", 0), EINVAL, -1); 459 } 460 461 462 PEXCEPTION_POINTERS old_pxcptinfoptrs = nullptr; 463 464 // If the action is global, we must acquire the lock before accessing it: 465 if (action_is_global) 466 __acrt_lock(__acrt_signal_lock); 467 468 __crt_signal_handler_t action = nullptr; 469 bool return0 = false; 470 __try 471 { 472 // Global function pointers are encoded; per-thread pointers are not: 473 action = action_is_global ? __crt_fast_decode_pointer(*action_pointer) : *action_pointer; 474 475 // If the current action is SIG_IGN, just return: 476 return0 = action == SIG_IGN; 477 if (return0) 478 __leave; 479 480 // If the current action is SIG_DFL, take the default action. The current 481 // default action for all of the supported signals is to terminate with an 482 // exit code of 3: 483 if (action == SIG_DFL) 484 { 485 // Be sure to unlock before entering the exit code. The exit function 486 // does not return. 487 if (action_is_global) 488 __acrt_unlock(__acrt_signal_lock); 489 490 _exit(3); 491 } 492 493 // For signals that correspond to exceptions, set the pointer to the 494 // EXCEPTION_POINTERS structure to nullptr: 495 if (signum == SIGFPE || signum == SIGSEGV || signum == SIGILL) 496 { 497 old_pxcptinfoptrs = ptd->_tpxcptinfoptrs; 498 ptd->_tpxcptinfoptrs = nullptr; 499 500 // If the signal is SIGFPE, also set _fpecode to _FPE_EXPLICITGEN: 501 if ( signum == SIGFPE ) 502 { 503 old_fpecode = _fpecode; 504 _fpecode = _FPE_EXPLICITGEN; 505 } 506 } 507 508 // Reset the action to SIG_DFL before calling the user-specified handler. 509 // For SIGFPE, we must reset the action for all of the FP exceptions: 510 if (signum == SIGFPE) 511 { 512 __crt_signal_action_t* const first = ptd->_pxcptacttab + __acrt_signal_action_first_fpe_index; 513 __crt_signal_action_t* const last = first + __acrt_signal_action_fpe_count; 514 515 for (__crt_signal_action_t* p = first; p != last; ++p) 516 { 517 p->_action = SIG_DFL; 518 } 519 } 520 else 521 { 522 *action_pointer = __crt_fast_encode_pointer(nullptr); 523 } 524 } 525 __finally 526 { 527 if (action_is_global) 528 __acrt_unlock(__acrt_signal_lock); 529 } 530 __endtry 531 532 if (return0) 533 return 0; 534 535 // Call the user-specified handler routine. For SIGFPE, we have special 536 // code to support old handlers which expect the value of _fpecode as the 537 // second argument: 538 if (signum == SIGFPE) 539 { 540 reinterpret_cast<void(__cdecl*)(int,int)>(action)(SIGFPE, _fpecode); 541 } 542 else 543 { 544 action(signum); 545 } 546 547 548 // For signals that correspond to exceptions, restore the pointer to the 549 // EXCEPTION_POINTERS structure: 550 if (signum == SIGFPE || signum == SIGSEGV || signum == SIGILL) 551 { 552 ptd->_tpxcptinfoptrs = old_pxcptinfoptrs; 553 554 // If signum is SIGFPE, also restore _fpecode 555 if (signum == SIGFPE) 556 _fpecode = old_fpecode; 557 } 558 559 return 0; 560} 561 562 563// Gets the SIGABRT signal handling function 564extern "C" __crt_signal_handler_t __cdecl __acrt_get_sigabrt_handler() 565{ 566 return __acrt_lock_and_call(__acrt_signal_lock, [] 567 { 568 return __crt_fast_decode_pointer(abort_action.value()); 569 }); 570} 571 572// Gets the FPE code for the current thread 573extern "C" int* __cdecl __fpecode() 574{ 575 return &__acrt_getptd()->_tfpecode; 576} 577 578// Returns a pointer to the signal handlers for the current thread 579extern "C" void** __cdecl __pxcptinfoptrs() 580{ 581 return reinterpret_cast<void**>(&__acrt_getptd()->_tpxcptinfoptrs); 582}