1 /***
2 *dbgrptt.c - Debug CRT Reporting Functions
3 *
4 * Copyright (c) Microsoft Corporation. All rights reserved.
5 *
6 *Purpose:
7 *
8 *******************************************************************************/
9
10 #ifndef _DEBUG
11 #error This file is supported only in debug builds
12 #define _DEBUG // For design-time support, when editing/viewing CRT sources
13 #endif
14
15 #include <corecrt_internal.h>
16 #include <errno.h>
17 #include <malloc.h>
18 #include <minmax.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21
22
23
24 extern "C" int __cdecl __acrt_MessageWindowA(
25 int report_type,
26 void* return_address,
27 char const* file_name,
28 char const* line_number,
29 char const* module_name,
30 char const* user_message
31 );
32
33 extern "C" int __cdecl __acrt_MessageWindowW(
34 int report_type,
35 void* return_address,
36 wchar_t const* file_name,
37 wchar_t const* line_number,
38 wchar_t const* module_name,
39 wchar_t const* user_message
40 );
41
42 extern "C" {
43
44 _CRT_REPORT_HOOK _pfnReportHook;
45
46 __crt_report_hook_node<char> *_pReportHookList;
47 __crt_report_hook_node<wchar_t> *_pReportHookListW;
48
49 long _crtAssertBusy = -1;
50
51 // Enclaves only support MODE_DEBUG for error output
52 #ifdef _UCRT_ENCLAVE_BUILD
53
54 int const _CrtDbgMode[_CRT_ERRCNT]
55 {
56 _CRTDBG_MODE_DEBUG,
57 _CRTDBG_MODE_DEBUG,
58 _CRTDBG_MODE_DEBUG
59 };
60
61 _HFILE const _CrtDbgFile[_CRT_ERRCNT]
62 {
63 _CRTDBG_INVALID_HFILE,
64 _CRTDBG_INVALID_HFILE,
65 _CRTDBG_INVALID_HFILE
66 };
67
68 #else
69
70 int _CrtDbgMode[_CRT_ERRCNT]
71 {
72 _CRTDBG_MODE_DEBUG,
73 _CRTDBG_MODE_WNDW,
74 _CRTDBG_MODE_WNDW
75 };
76
77 _HFILE _CrtDbgFile[_CRT_ERRCNT]
78 {
79 _CRTDBG_INVALID_HFILE,
80 _CRTDBG_INVALID_HFILE,
81 _CRTDBG_INVALID_HFILE
82 };
83
84 /***
85 *int _CrtSetReportMode - set the reporting mode for a given report type
86 *
87 *Purpose:
88 * set the reporting mode for a given report type
89 *
90 *Entry:
91 * int nRptType - the report type
92 * int fMode - new mode for given report type
93 *
94 *Exit:
95 * previous mode for given report type
96 *
97 *Exceptions:
98 * Input parameters are validated. Refer to the validation section of the function.
99 *
100 *******************************************************************************/
_CrtSetReportMode(int nRptType,int fMode)101 int __cdecl _CrtSetReportMode(
102 int nRptType,
103 int fMode
104 )
105 {
106 int oldMode;
107
108 /* validation section */
109 _VALIDATE_RETURN(nRptType >= 0 && nRptType < _CRT_ERRCNT, EINVAL, -1);
110 _VALIDATE_RETURN(
111 fMode == _CRTDBG_REPORT_MODE ||
112 (fMode & ~(_CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG | _CRTDBG_MODE_WNDW)) == 0,
113 EINVAL,
114 -1);
115
116 if (fMode == _CRTDBG_REPORT_MODE)
117 return _CrtDbgMode[nRptType];
118
119 oldMode = _CrtDbgMode[nRptType];
120
121 _CrtDbgMode[nRptType] = fMode;
122
123 return oldMode;
124 }
125
126 /***
127 *int _CrtSetReportFile - set the reporting file for a given report type
128 *
129 *Purpose:
130 * set the reporting file for a given report type
131 *
132 *Entry:
133 * int nRptType - the report type
134 * _HFILE hFile - new file for given report type
135 *
136 *Exit:
137 * previous file for given report type
138 *
139 *Exceptions:
140 * Input parameters are validated. Refer to the validation section of the function.
141 *
142 *******************************************************************************/
_CrtSetReportFile(int nRptType,_HFILE hFile)143 _HFILE __cdecl _CrtSetReportFile(
144 int nRptType,
145 _HFILE hFile
146 )
147 {
148 _HFILE oldFile;
149
150 /* validation section */
151 _VALIDATE_RETURN(nRptType >= 0 && nRptType < _CRT_ERRCNT, EINVAL, _CRTDBG_HFILE_ERROR);
152
153 if (hFile == _CRTDBG_REPORT_FILE)
154 return _CrtDbgFile[nRptType];
155
156 oldFile = _CrtDbgFile[nRptType];
157
158 if (_CRTDBG_FILE_STDOUT == hFile)
159 _CrtDbgFile[nRptType] = GetStdHandle(STD_OUTPUT_HANDLE);
160 else if (_CRTDBG_FILE_STDERR == hFile)
161 _CrtDbgFile[nRptType] = GetStdHandle(STD_ERROR_HANDLE);
162 else
163 _CrtDbgFile[nRptType] = hFile;
164
165 return oldFile;
166 }
167
168 #endif /* _UCRT_ENCLAVE_BUILD */
169
170 /***
171 *_CRT_REPORT_HOOK _CrtSetReportHook() - set client report hook
172 *
173 *Purpose:
174 * set client report hook. This function is provided only in ANSI
175 * for backward compatibility. No Unicode Version of this function exists
176 *
177 *Entry:
178 * _CRT_REPORT_HOOK pfnNewHook - new report hook
179 *
180 *Exit:
181 * return previous hook
182 *
183 *Exceptions:
184 *
185 *******************************************************************************/
186
_CrtSetReportHook(_CRT_REPORT_HOOK pfnNewHook)187 _CRT_REPORT_HOOK __cdecl _CrtSetReportHook(_CRT_REPORT_HOOK pfnNewHook)
188 {
189 _CRT_REPORT_HOOK pfnOldHook = _pfnReportHook;
190 _pfnReportHook = pfnNewHook;
191 return pfnOldHook;
192 }
193
194 /***
195 *_CRT_REPORT_HOOK _CrtGetReportHook() - get client report hook
196 *
197 *Purpose:
198 * get client report hook.
199 *
200 *Entry:
201 *
202 *Exit:
203 * return current hook
204 *
205 *Exceptions:
206 *
207 *******************************************************************************/
208
_CrtGetReportHook(void)209 _CRT_REPORT_HOOK __cdecl _CrtGetReportHook(void)
210 {
211 return _pfnReportHook;
212 }
213
214 #define ASSERTINTRO1 "Assertion failed: "
215 #define ASSERTINTRO2 "Assertion failed!"
216
217 /***
218 *int _VCrtDbgReportA() - _CrtDbgReport calls into this function
219 *
220 *Purpose:
221 * See remarks for _CrtDbgReport.
222 *
223 *Entry:
224 * int nRptType - report type
225 * char const* szFile - file name
226 * int nLine - line number
227 * char const* szModule - module name
228 * char const* szFormat - format string
229 * va_list arglist - var args arglist
230 *
231 *Exit:
232 * See remarks for _CrtDbgReport
233 *
234 *Exceptions:
235 *
236 *******************************************************************************/
237
238 #pragma warning(push)
239 #pragma warning(disable:6262)
240 // prefast (6262): This func uses lots of stack because we want to tolerate very large reports, and we can't use malloc here.
_VCrtDbgReportA(int nRptType,void * returnAddress,char const * szFile,int nLine,char const * szModule,char const * szFormat,va_list arglist)241 int __cdecl _VCrtDbgReportA(
242 int nRptType,
243 void * returnAddress,
244 char const* szFile,
245 int nLine,
246 char const* szModule,
247 char const* szFormat,
248 va_list arglist
249 )
250 {
251 int retval=0;
252 int handled=FALSE;
253
254 char szLineMessage[DBGRPT_MAX_MSG]{0};
255 char szOutMessage [DBGRPT_MAX_MSG]{0};
256 wchar_t szOutMessage2[DBGRPT_MAX_MSG]{0};
257 char szUserMessage[DBGRPT_MAX_MSG]{0};
258
259 if (nRptType < 0 || nRptType >= _CRT_ERRCNT)
260 return -1;
261
262 /*
263 * handle the (hopefully rare) case of
264 *
265 * 1) ASSERT while already dealing with an ASSERT
266 * or
267 * 2) two threads asserting at the same time
268 */
269
270 __try
271 {
272 if (_CRT_ASSERT == nRptType && _InterlockedIncrement(&_crtAssertBusy) > 0)
273 {
274 /* use only 'safe' functions -- must not assert in here! */
275
276 _ERRCHECK(_itoa_s(nLine, szLineMessage, DBGRPT_MAX_MSG, 10));
277
278 __acrt_OutputDebugStringA("Second Chance Assertion Failed: File ");
279 __acrt_OutputDebugStringA(szFile ? szFile : "<file unknown>");
280 __acrt_OutputDebugStringA(", Line ");
281 __acrt_OutputDebugStringA(szLineMessage);
282 __acrt_OutputDebugStringA("\n");
283
284 _CrtDbgBreak();
285 retval=-1;
286 __leave;
287 }
288
289 // Leave space for ASSERTINTRO1 and "\r\n"
290 if (szFormat)
291 {
292 int szlen = 0;
293 _ERRCHECK_SPRINTF(szlen = _vsnprintf_s(szUserMessage, DBGRPT_MAX_MSG,
294 DBGRPT_MAX_MSG - 2- max(sizeof(ASSERTINTRO1),sizeof(ASSERTINTRO2)),
295 szFormat, arglist));
296 if (szlen < 0)
297 {
298 _ERRCHECK(strcpy_s(szUserMessage, DBGRPT_MAX_MSG, DBGRPT_TOOLONGMSG));
299 }
300 }
301
302 if (_CRT_ASSERT == nRptType)
303 {
304 _ERRCHECK(strcpy_s(szLineMessage, DBGRPT_MAX_MSG, szFormat ? ASSERTINTRO1 : ASSERTINTRO2));
305 }
306
307 _ERRCHECK(strcat_s(szLineMessage, DBGRPT_MAX_MSG, szUserMessage));
308
309 if (_CRT_ASSERT == nRptType)
310 {
311 if (_CrtDbgMode[nRptType] & _CRTDBG_MODE_FILE)
312 {
313 _ERRCHECK(strcat_s(szLineMessage, DBGRPT_MAX_MSG, "\r"));
314 }
315
316 _ERRCHECK(strcat_s(szLineMessage, DBGRPT_MAX_MSG, "\n"));
317 }
318
319 if (szFile)
320 {
321 int szlen = 0;
322 _ERRCHECK_SPRINTF(szlen = _snprintf_s(szOutMessage, DBGRPT_MAX_MSG, DBGRPT_MAX_MSG - 1, "%s(%d) : %s",
323 szFile, nLine, szLineMessage));
324 if (szlen < 0)
325 {
326 _ERRCHECK(strcpy_s(szOutMessage, DBGRPT_MAX_MSG, DBGRPT_TOOLONGMSG));
327 }
328 }
329 else
330 {
331 _ERRCHECK(strcpy_s(szOutMessage, DBGRPT_MAX_MSG, szLineMessage));
332 }
333
334 {
335 size_t ret = 0;
336 errno_t e = 0;
337 _ERRCHECK_EINVAL_ERANGE(e = mbstowcs_s(&ret, szOutMessage2, DBGRPT_MAX_MSG, szOutMessage, _TRUNCATE));
338 if(e != 0)
339 {
340 _ERRCHECK(wcscpy_s(szOutMessage2, DBGRPT_MAX_MSG, _CRT_WIDE(DBGRPT_INVALIDMSG)));
341 }
342 }
343
344 /* User hook may handle report.
345 We have to check the ANSI Hook2 List & then the UNICODE Hook2 List.
346 Then we have check any ANSI individual Hook set through
347 SetReportHook */
348
349 if (_pReportHookList || _pReportHookListW)
350 {
351 __crt_report_hook_node<char> *pnode=nullptr;
352 __crt_report_hook_node<wchar_t> *pnodeW=nullptr;
353
354 __acrt_lock(__acrt_debug_lock);
355 __try
356 {
357 for (pnode = _pReportHookList; pnode; pnode = pnode->next)
358 {
359 int hook_retval=0;
360 if (pnode->hook(nRptType, szOutMessage, &hook_retval))
361 {
362 handled=TRUE;
363 retval=hook_retval;
364 __leave;
365 }
366 }
367
368 for (pnodeW = _pReportHookListW; pnodeW; pnodeW = pnodeW->next)
369 {
370 int hook_retval=0;
371 if (pnodeW->hook(nRptType, szOutMessage2, &hook_retval))
372 {
373 handled=TRUE;
374 retval=hook_retval;
375 __leave;
376 }
377 }
378 }
379 __finally
380 {
381 __acrt_unlock(__acrt_debug_lock);
382 }
383 __endtry
384 }
385
386 if (handled)
387 __leave;
388
389 if (_pfnReportHook)
390 {
391 int hook_retval=0;
392 if (_pfnReportHook(nRptType, szOutMessage, &hook_retval))
393 {
394 retval = hook_retval;
395 __leave;
396 }
397 }
398
399 if (_CrtDbgMode[nRptType] & _CRTDBG_MODE_FILE)
400 {
401 if (_CrtDbgFile[nRptType] != _CRTDBG_INVALID_HFILE)
402 {
403 DWORD bytes_written = 0;
404 WriteFile(_CrtDbgFile[nRptType], szOutMessage, static_cast<DWORD>(strlen(szOutMessage)), &bytes_written, nullptr);
405 }
406 }
407
408 if (_CrtDbgMode[nRptType] & _CRTDBG_MODE_DEBUG)
409 {
410 __acrt_OutputDebugStringA(szOutMessage);
411 }
412
413 if (_CrtDbgMode[nRptType] & _CRTDBG_MODE_WNDW)
414 {
415 szLineMessage[0] = 0;
416 if (nLine)
417 {
418 _ERRCHECK(_itoa_s(nLine, szLineMessage, DBGRPT_MAX_MSG, 10));
419 }
420
421 retval = __acrt_MessageWindowA(nRptType, returnAddress, szFile, (nLine ? szLineMessage : nullptr), szModule, szUserMessage);
422 }
423 }
424 __finally
425 {
426 if (_CRT_ASSERT == nRptType)
427 {
428 _InterlockedDecrement(&_crtAssertBusy);
429 }
430 }
431 __endtry
432
433 return retval;
434 }
435 #pragma warning(pop)
436
437 /***
438 *int _VCrtDbgReportW() - _CrtDbgReportW calls into this function
439 *
440 *Purpose:
441 * See remarks for _CrtDbgReport.
442 *
443 *Entry:
444 * int nRptType - report type
445 * wchar_t const* szFile - file name
446 * int nLine - line number
447 * wchar_t const* szModule - module name
448 * wchar_t const* szFormat - format string
449 * va_list arglist - var args arglist
450 *
451 *Exit:
452 * See remarks for _CrtDbgReport
453 *
454 *Exceptions:
455 *
456 *******************************************************************************/
457
458 #pragma warning(push)
459 #pragma warning(disable:6262)
460 // prefast(6262): This func uses lots of stack because we want to tolerate very large reports, and we can't use malloc here.
_VCrtDbgReportW(int nRptType,void * returnAddress,wchar_t const * szFile,int nLine,wchar_t const * szModule,wchar_t const * szFormat,va_list arglist)461 int __cdecl _VCrtDbgReportW
462 (
463 int nRptType,
464 void * returnAddress,
465 wchar_t const* szFile,
466 int nLine,
467 wchar_t const* szModule,
468 wchar_t const* szFormat,
469 va_list arglist
470 )
471 {
472 int retval=0;
473 int handled=FALSE;
474 wchar_t szLineMessage[DBGRPT_MAX_MSG] = {0};
475 wchar_t szOutMessage[DBGRPT_MAX_MSG] = {0};
476 char szOutMessage2[DBGRPT_MAX_MSG] = {0};
477 wchar_t szUserMessage[DBGRPT_MAX_MSG] = {0};
478
479 if (nRptType < 0 || nRptType >= _CRT_ERRCNT)
480 return -1;
481
482 /*
483 * handle the (hopefully rare) case of
484 *
485 * 1) ASSERT while already dealing with an ASSERT
486 * or
487 * 2) two threads asserting at the same time
488 */
489
490 __try
491 {
492 if (_CRT_ASSERT == nRptType && _InterlockedIncrement(&_crtAssertBusy) > 0)
493 {
494 /* use only 'safe' functions -- must not assert in here! */
495
496 _ERRCHECK(_itow_s(nLine, szLineMessage, DBGRPT_MAX_MSG, 10));
497
498 OutputDebugStringW(L"Second Chance Assertion Failed: File ");
499 OutputDebugStringW(szFile ? szFile : L"<file unknown>");
500 OutputDebugStringW(L", Line ");
501 OutputDebugStringW(szLineMessage);
502 OutputDebugStringW(L"\n");
503
504 _CrtDbgBreak();
505 retval = -1;
506 __leave;
507 }
508
509 if (szFormat)
510 {
511 // Leave space for ASSERTINTRO{1,2} and "\r\n"
512 size_t const max_assert_intro_count = __max(_countof(ASSERTINTRO1), _countof(ASSERTINTRO2));
513 size_t const max_user_message_count = _countof(szUserMessage) - 2 - max_assert_intro_count;
514
515 // Force use of the legacy stdio wide character format specifiers
516 // mode for source compatibility. If we ever revisit support for
517 // the standard format specifiers, we'll need to revisit this as
518 // well.
519 int szlen = 0;
520 _ERRCHECK_SPRINTF(szlen = __stdio_common_vsnwprintf_s(
521 _CRT_INTERNAL_PRINTF_LEGACY_WIDE_SPECIFIERS,
522 szUserMessage,
523 _countof(szUserMessage),
524 max_user_message_count,
525 szFormat,
526 nullptr,
527 arglist));
528 if (szlen < 0)
529 {
530 _ERRCHECK(wcscpy_s(szUserMessage, DBGRPT_MAX_MSG, _CRT_WIDE(DBGRPT_TOOLONGMSG)));
531 }
532 }
533
534 if (_CRT_ASSERT == nRptType)
535 _ERRCHECK(wcscpy_s(szLineMessage, DBGRPT_MAX_MSG, szFormat ? _CRT_WIDE(ASSERTINTRO1) : _CRT_WIDE(ASSERTINTRO2)));
536
537 _ERRCHECK(wcscat_s(szLineMessage, DBGRPT_MAX_MSG, szUserMessage));
538
539 if (_CRT_ASSERT == nRptType)
540 {
541 if (_CrtDbgMode[nRptType] & _CRTDBG_MODE_FILE)
542 _ERRCHECK(wcscat_s(szLineMessage, DBGRPT_MAX_MSG, L"\r"));
543 {
544 _ERRCHECK(wcscat_s(szLineMessage, DBGRPT_MAX_MSG, L"\n"));
545 }
546 }
547
548 if (szFile)
549 {
550 int szlen = 0;
551 _ERRCHECK_SPRINTF(szlen = _snwprintf_s(szOutMessage, DBGRPT_MAX_MSG, DBGRPT_MAX_MSG, L"%ls(%d) : %ls",
552 szFile, nLine, szLineMessage));
553 if (szlen < 0)
554 _ERRCHECK(wcscpy_s(szOutMessage, DBGRPT_MAX_MSG, _CRT_WIDE(DBGRPT_TOOLONGMSG)));
555 }
556 else
557 {
558 _ERRCHECK(wcscpy_s(szOutMessage, DBGRPT_MAX_MSG, szLineMessage));
559 }
560
561 /* scope */
562 {
563 errno_t e = _ERRCHECK_EINVAL_ERANGE(wcstombs_s(nullptr, szOutMessage2, DBGRPT_MAX_MSG, szOutMessage, _TRUNCATE));
564 if(e != 0)
565 _ERRCHECK(strcpy_s(szOutMessage2, DBGRPT_MAX_MSG, DBGRPT_INVALIDMSG));
566 }
567
568 /* User hook may handle report.
569 We have to check the ANSI Hook2 List & then the UNICODE Hook2 List.
570 Then we have check any ANSI individual Hook set through
571 SetReportHook */
572
573 if (_pReportHookList || _pReportHookListW)
574 {
575 __crt_report_hook_node<char> *pnode=nullptr;
576 __crt_report_hook_node<wchar_t> *pnodeW=nullptr;
577
578 __acrt_lock(__acrt_debug_lock);
579 __try
580 {
581 for (pnode = _pReportHookList; pnode; pnode = pnode->next)
582 {
583 int hook_retval=0;
584 if (pnode->hook(nRptType, szOutMessage2, &hook_retval))
585 {
586 retval=hook_retval;
587 handled=TRUE;
588 __leave;
589 }
590 }
591
592 for (pnodeW = _pReportHookListW; pnodeW; pnodeW = pnodeW->next)
593 {
594 int hook_retval=0;
595 if (pnodeW->hook(nRptType, szOutMessage, &hook_retval))
596 {
597 retval=hook_retval;
598 handled=TRUE;
599 __leave;
600 }
601 }
602 }
603 __finally
604 {
605 __acrt_unlock(__acrt_debug_lock);
606 }
607 __endtry
608 }
609
610 if (handled)
611 __leave;
612
613 if(_pfnReportHook)
614 {
615 int hook_retval=0;
616 if (_pfnReportHook(nRptType, szOutMessage2, &hook_retval))
617 {
618 retval = hook_retval;
619 __leave;
620 }
621 }
622
623 if ((_CrtDbgMode[nRptType] & _CRTDBG_MODE_FILE) && _CrtDbgFile[nRptType] != _CRTDBG_INVALID_HFILE)
624 {
625 /* Use WriteConsole for Consoles, WriteFile otherwise */
626 switch (GetFileType(_CrtDbgFile[nRptType]))
627 {
628 case FILE_TYPE_CHAR:
629 {
630 DWORD characters_written = 0;
631 if (WriteConsoleW(_CrtDbgFile[nRptType], szOutMessage, static_cast<DWORD>(wcslen(szOutMessage)), &characters_written, nullptr))
632 break;
633
634 /* If WriteConsole fails & LastError is ERROR_INVALID_VALUE, then the console is redirected */
635 if (GetLastError() != ERROR_INVALID_HANDLE)
636 break;
637 }
638 default:
639 {
640 char szaOutMessage[DBGRPT_MAX_MSG];
641 size_t ret = 0;
642 errno_t e = _ERRCHECK_EINVAL_ERANGE(wcstombs_s(&ret, szaOutMessage, DBGRPT_MAX_MSG, szOutMessage, _TRUNCATE));
643
644 if (e != 0 && e != STRUNCATE)
645 {
646 DWORD bytes_written = 0;
647 WriteFile(_CrtDbgFile[nRptType], szOutMessage, static_cast<DWORD>(wcslen(szOutMessage)) * 2, &bytes_written, nullptr);
648 }
649 else
650 {
651 /* ret counts for the null terminator as well */
652 if (ret > 0)
653 {
654 --ret;
655 }
656
657 DWORD bytes_written = 0;
658 WriteFile(_CrtDbgFile[nRptType], szaOutMessage, static_cast<DWORD>(ret), &bytes_written, nullptr);
659 }
660 }
661 }
662 }
663
664 if (_CrtDbgMode[nRptType] & _CRTDBG_MODE_DEBUG)
665 {
666 ::OutputDebugStringW(szOutMessage);
667 }
668
669 if (_CrtDbgMode[nRptType] & _CRTDBG_MODE_WNDW)
670 {
671 szLineMessage[0] = 0;
672 if (nLine)
673 {
674 _ERRCHECK(_itow_s(nLine, szLineMessage, DBGRPT_MAX_MSG, 10));
675 }
676 retval = __acrt_MessageWindowW(nRptType, returnAddress, szFile, (nLine ? szLineMessage : nullptr), szModule, szUserMessage);
677 }
678 }
679 __finally
680 {
681 if (_CRT_ASSERT == nRptType)
682 {
683 _InterlockedDecrement(&_crtAssertBusy);
684 }
685 }
686 __endtry
687
688 return retval;
689 }
690 #pragma warning(pop)
691
692 } // extern "C"
693