1 /*** 2 *dbgrpt.c - Debug CRT Reporting Functions 3 * 4 * Copyright (c) Microsoft Corporation. All rights reserved. 5 * 6 *Purpose: 7 * 8 *******************************************************************************/ 9 #ifndef _DEBUG 10 #error This file is supported only in debug builds 11 #endif 12 13 #include <corecrt_internal.h> 14 #include <corecrt_internal_traits.h> 15 #include <intrin.h> 16 #include <malloc.h> 17 #include <minmax.h> 18 #include <signal.h> 19 #include <stdarg.h> 20 #include <stdio.h> 21 #include <stdlib.h> 22 23 24 /*--------------------------------------------------------------------------- 25 * 26 * Debug Reporting 27 * 28 --------------------------------------------------------------------------*/ 29 30 extern "C" _CRT_REPORT_HOOK _pfnReportHook; 31 extern "C" __crt_report_hook_node<char>* _pReportHookList; 32 extern "C" __crt_report_hook_node<wchar_t>* _pReportHookListW; 33 34 static __crt_report_hook_node<char>*& __cdecl get_report_hook_list(char) throw() { return _pReportHookList; } 35 static __crt_report_hook_node<wchar_t>*& __cdecl get_report_hook_list(wchar_t) throw() { return _pReportHookListW; } 36 37 static wchar_t const* const report_type_messages[_CRT_ERRCNT] = 38 { 39 L"Warning", 40 L"Error", 41 L"Assertion Failed" 42 }; 43 44 // Enclaves only support MODE_DEBUG for error output 45 #ifndef _UCRT_ENCLAVE_BUILD 46 47 static wchar_t const* __cdecl get_output_message_format(char) throw() 48 { 49 return L"Debug %ls!\n\nProgram: %hs%ls%ls%hs%ls%hs%ls%hs%ls%ls%hs%ls\n\n(Press Retry to debug the application)\n"; 50 } 51 52 static wchar_t const* __cdecl get_output_message_format(wchar_t) throw() 53 { 54 return L"Debug %ls!\n\nProgram: %ls%ls%ls%ls%ls%ls%ls%ls%ls%ls%ls%ls\n\n(Press Retry to debug the application)\n"; 55 } 56 57 static wchar_t const* const more_info_string = 58 L"\n\nFor information on how your program can cause an assertion" 59 L"\nfailure, see the Visual C++ documentation on asserts."; 60 61 _GENERATE_TCHAR_STRING_FUNCTIONS(program_name_unknown_text, "<program name unknown>") 62 63 #endif /* _UCRT_ENCLAVE_BUILD */ 64 65 /*** 66 *_CRT_REPORT_HOOK _CrtSetReportHook2() - configure client report hook in list 67 * 68 *Purpose: 69 * Install or remove a client report hook from the report list. Exists 70 * separately from _CrtSetReportHook because the older function doesn't 71 * work well in an environment where DLLs that are loaded and unloaded 72 * dynamically out of LIFO order want to install report hooks. 73 * This function exists in 2 forms - ANSI & UNICODE 74 * 75 *Entry: 76 * int mode - _CRT_RPTHOOK_INSTALL or _CRT_RPTHOOK_REMOVE 77 * _CRT_REPORT_HOOK pfnNewHook - report hook to install/remove/query 78 * 79 *Exit: 80 * Returns -1 if an error was encountered, with EINVAL or ENOMEM set, 81 * else returns the reference count of pfnNewHook after the call. 82 * 83 *Exceptions: 84 * Input parameters are validated. Refer to the validation section of the function. 85 * 86 *******************************************************************************/ 87 template <typename Character, typename Hook> 88 static int __cdecl common_set_report_hook( 89 int const mode, 90 Hook const new_hook 91 ) throw() 92 { 93 using node_type = __crt_report_hook_node<Character>; 94 95 _VALIDATE_RETURN(mode == _CRT_RPTHOOK_INSTALL || mode == _CRT_RPTHOOK_REMOVE, EINVAL, -1); 96 _VALIDATE_RETURN(new_hook != nullptr, EINVAL, -1); 97 98 return __acrt_lock_and_call(__acrt_debug_lock, [&] 99 { 100 node_type*& hook_list = get_report_hook_list(Character()); 101 102 node_type* p; 103 int ret = 0; 104 105 // Search for new hook function to see if it's already installed 106 for (p = hook_list; p != nullptr; p = p->next) 107 if (p->hook == new_hook) 108 break; 109 110 if (mode == _CRT_RPTHOOK_REMOVE) 111 { 112 // Remove request - free list node if refcount goes to zero 113 if (p != nullptr) 114 { 115 if ((ret = --p->refcount) == 0) 116 { 117 if (p->next) 118 { 119 p->next->prev = p->prev; 120 } 121 if (p->prev) 122 { 123 p->prev->next = p->next; 124 } 125 else 126 { 127 hook_list = p->next; 128 } 129 130 _free_crt(p); 131 } 132 } 133 else 134 { 135 _ASSERTE(("The hook function is not in the list!", 0)); 136 errno = EINVAL; 137 return -1; 138 } 139 } 140 else 141 { 142 // Insert request 143 if (p != nullptr) 144 { 145 // Hook function already registered, move to head of list 146 ret = ++p->refcount; 147 if (p != hook_list) 148 { 149 if (p->next) 150 p->next->prev = p->prev; 151 p->prev->next = p->next; 152 p->prev = nullptr; 153 p->next = hook_list; 154 hook_list->prev = p; 155 hook_list = p; 156 } 157 } 158 else 159 { 160 // Hook function not already registered, insert new node 161 __crt_unique_heap_ptr<node_type> new_node(_calloc_crt_t(node_type, 1)); 162 if (!new_node) 163 { 164 ret = -1; 165 errno = ENOMEM; 166 } 167 else 168 { 169 new_node.get()->prev = nullptr; 170 new_node.get()->next = hook_list; 171 172 if (hook_list) 173 { 174 hook_list->prev = new_node.get(); 175 } 176 177 ret = new_node.get()->refcount = 1; 178 new_node.get()->hook = new_hook; 179 hook_list = new_node.detach(); 180 } 181 } 182 } 183 184 return ret; 185 }); 186 } 187 188 extern "C" int __cdecl _CrtSetReportHook2( 189 int const mode, 190 _CRT_REPORT_HOOK const new_hook 191 ) 192 { 193 return common_set_report_hook<char>(mode, new_hook); 194 } 195 196 extern "C" int __cdecl _CrtSetReportHookW2( 197 int const mode, 198 _CRT_REPORT_HOOKW const new_hook 199 ) 200 { 201 return common_set_report_hook<wchar_t>(mode, new_hook); 202 } 203 204 #define MAXLINELEN 64 205 206 /*** 207 *int _CrtDbgReport() - primary reporting function 208 * 209 *Purpose: 210 * Display a message window with the following format. 211 * 212 * ================= Microsft Visual C++ Debug Library ================ 213 * 214 * {Warning! | Error! | Assertion Failed!} 215 * 216 * Program: c:\test\mytest\foo.exe 217 * [Module: c:\test\mytest\bar.dll] 218 * [File: c:\test\mytest\bar.c] 219 * [Line: 69] 220 * 221 * {<warning or error message> | Expression: <expression>} 222 * 223 * [For information on how your program can cause an assertion 224 * failure, see the Visual C++ documentation on asserts] 225 * 226 * (Press Retry to debug the application) 227 * 228 * =================================================================== 229 * 230 *Entry: 231 * int nRptType - report type 232 * void * returnAddress - return address of caller 233 * const TCHAR * szFile - file name 234 * int nLine - line number 235 * const TCHAR * szModule - module name 236 * const TCHAR * szFormat - format string 237 * ... - var args 238 * 239 *Exit: 240 * if (MessageBox) 241 * { 242 * Abort -> aborts 243 * Retry -> return TRUE 244 * Ignore-> return FALSE 245 * } 246 * else 247 * return FALSE 248 * 249 *Exceptions: 250 * If something goes wrong, we do not assert, but we return -1. 251 * 252 *******************************************************************************/ 253 extern "C" int __cdecl _CrtDbgReport( 254 int const report_type, 255 char const* const file_name, 256 int const line_number, 257 char const* const module_name, 258 char const* const format, 259 ...) 260 { 261 va_list arglist; 262 va_start(arglist, format); 263 int const result = _VCrtDbgReportA(report_type, _ReturnAddress(), file_name, line_number, module_name, format, arglist); 264 va_end(arglist); 265 return result; 266 } 267 268 extern "C" int __cdecl _CrtDbgReportW( 269 int const report_type, 270 wchar_t const* const file_name, 271 int const line_number, 272 wchar_t const* const module_name, 273 wchar_t const* const format, 274 ...) 275 { 276 va_list arglist; 277 va_start(arglist, format); 278 int const result = _VCrtDbgReportW(report_type, _ReturnAddress(), file_name, line_number, module_name, format, arglist); 279 va_end(arglist); 280 return result; 281 } 282 283 // Enclaves only support MODE_DEBUG for error output 284 #if !defined _UCRT_ENCLAVE_BUILD 285 286 /*** 287 *int __crtMessageWindow() - report to a message window 288 * 289 *Purpose: 290 * put report into message window, allow user to choose action to take 291 * 292 *Entry: 293 * int nRptType - report type 294 * const TCHAR * szFile - file name 295 * const TCHAR * szLine - line number 296 * const TCHAR * szModule - module name 297 * const TCHAR * szUserMessage - user message 298 * 299 *Exit: 300 * if (MessageBox) 301 * { 302 * Abort -> aborts 303 * Retry -> return TRUE 304 * Ignore-> return FALSE 305 * } 306 * else 307 * return FALSE 308 * 309 *Exceptions: 310 * If something goes wrong, we do not assert, but we simply return -1, 311 * which will trigger the debugger automatically (the same as the user 312 * pressing the Retry button). 313 * 314 *******************************************************************************/ 315 template <typename Character> 316 static int __cdecl common_message_window( 317 int const report_type, 318 void* const return_address, 319 Character const* const file_name, 320 Character const* const line_number, 321 Character const* const module_name, 322 Character const* const user_message 323 ) throw() 324 { 325 using traits = __crt_char_traits<Character>; 326 327 if (user_message == nullptr) 328 { 329 return 1; 330 } 331 332 HMODULE module = nullptr; 333 if (!GetModuleHandleExW( 334 GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT | GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, 335 static_cast<LPCWSTR>(return_address), 336 &module)) 337 { 338 module = nullptr; 339 } 340 #ifdef CRTDLL 341 else if (module == reinterpret_cast<HMODULE>(&__ImageBase)) 342 { 343 // If the message was reported from within the CRT DLL, report it as 344 // having come from the EXE instead: 345 module = nullptr; 346 } 347 #endif 348 349 Character program_name[MAX_PATH + 1]{}; 350 if (!traits::get_module_file_name(module, program_name, static_cast<DWORD>(_countof(program_name)))) 351 { 352 _ERRCHECK(traits::tcscpy_s(program_name, _countof(program_name), get_program_name_unknown_text(Character()))); 353 } 354 355 // Shorten the program name: 356 size_t const program_name_length = traits::tcslen(program_name); 357 Character* short_program_name = program_name; 358 if (program_name_length > MAXLINELEN) 359 { 360 short_program_name += program_name_length - MAXLINELEN; 361 static_assert(MAXLINELEN > 3, ""); 362 #pragma warning(push) 363 #pragma warning(disable:__WARNING_POTENTIAL_BUFFER_OVERFLOW_HIGH_PRIORITY) // 26015 364 short_program_name[0] = '.'; 365 short_program_name[1] = '.'; 366 short_program_name[2] = '.'; 367 #pragma warning(pop) 368 } 369 370 // Shorten the module name: 371 size_t const module_name_length = module_name ? traits::tcslen(module_name) : 0; 372 Character const* short_module_name = nullptr; 373 if (module_name && module_name_length > MAXLINELEN) 374 { 375 short_module_name = module_name + module_name_length - MAXLINELEN + 3; 376 } 377 378 static Character const empty_string[] = { '\0' }; 379 380 wchar_t message_buffer[DBGRPT_MAX_MSG]; 381 int const sprintf_result = _snwprintf_s( 382 message_buffer, 383 _countof(message_buffer), 384 _countof(message_buffer) - 1, 385 386 get_output_message_format(Character()), 387 388 report_type_messages[report_type], 389 short_program_name, 390 module_name ? L"\nModule: " : L"", 391 short_module_name ? L"..." : L"", 392 short_module_name ? short_module_name : (module_name ? module_name : empty_string), 393 file_name ? L"\nFile: " : L"", 394 file_name ? file_name : empty_string, 395 line_number ? L"\nLine: " : L"", 396 line_number ? line_number : empty_string, 397 user_message[0] ? L"\n\n" : L"", 398 user_message[0] && _CRT_ASSERT == report_type ? L"Expression: " : L"", 399 user_message[0] ? user_message : empty_string, 400 _CRT_ASSERT == report_type ? more_info_string : L""); 401 402 _ERRCHECK_SPRINTF(sprintf_result); 403 404 if (sprintf_result < 0) 405 { 406 _ERRCHECK(wcscpy_s(message_buffer, DBGRPT_MAX_MSG, _CRT_WIDE(DBGRPT_TOOLONGMSG))); 407 } 408 409 int const message_box_result = __acrt_show_wide_message_box( 410 message_buffer, 411 L"Microsoft Visual C++ Runtime Library", 412 MB_TASKMODAL | MB_ICONHAND | MB_ABORTRETRYIGNORE | MB_SETFOREGROUND); 413 414 switch (message_box_result) 415 { 416 case IDABORT: // Abort: Terminate execution 417 { 418 // Note that even though we are "aborting," we do not call abort() 419 // because we do not want to invoke Watson (the user has already had an 420 // opportunity to debug the error and chose not to). 421 __crt_signal_handler_t const sigabrt_action = __acrt_get_sigabrt_handler(); 422 if (sigabrt_action != SIG_DFL) 423 { 424 raise(SIGABRT); 425 } 426 427 TerminateProcess(GetCurrentProcess(), 3); 428 } 429 430 case IDRETRY: 431 { 432 return 1; // Retry: Debug break 433 } 434 435 case IDIGNORE: 436 default: 437 { 438 return 0; // Ignore: Continue execution 439 } 440 } 441 } 442 443 extern "C" int __cdecl __acrt_MessageWindowA( 444 int const report_type, 445 void* const return_address, 446 char const* const file_name, 447 char const* const line_number, 448 char const* const module_name, 449 char const* const user_message 450 ) 451 { 452 return common_message_window(report_type, return_address, file_name, line_number, module_name, user_message); 453 } 454 455 extern "C" int __cdecl __acrt_MessageWindowW( 456 int const report_type, 457 void* const return_address, 458 wchar_t const* const file_name, 459 wchar_t const* const line_number, 460 wchar_t const* const module_name, 461 wchar_t const* const user_message 462 ) 463 { 464 return common_message_window(report_type, return_address, file_name, line_number, module_name, user_message); 465 } 466 467 #endif 468