windbg crashdump 1

open crash dump in windbg

open crash dump in windbg

 

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
Windows 10 Version 18362 MP (4 procs) Free x64
Product: WinNt, suite: SingleUserTS
Built by: 10.0.18362.329 (WinBuild.160101.0800)
Machine Name:
Debug session time: Thu Nov 21 18:32:37.000 2019 (UTC - 8:00)
System Uptime: 3 days 22:27:41.943
Process Uptime: 0 days 0:00:01.000
............
This dump file has an exception of interest stored in it.
The stored exception information can be accessed via .ecxr.
(62c.2a10): Security check failure or stack buffer overrun - code c0000409 (first/second chance not available)
ucrtbase!abort+0x4e:
00007ffb`5997db8e cd29            int     29h


First thing read thru this text, it often provides good enough information. Here windbg is warning you stored exception information in .excr. Generally you start with either .excr command or start with !analyze -v extension.

.cls command clears the window if gets too cluttered

!analyze -v dumps a lot of text, lets take it step by step:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
0:009> !analyze -v
*******************************************************************************
*                                                                             *
*                        Exception Analysis                                   *
*                                                                             *
*******************************************************************************

*** WARNING: Unable to verify checksum for tbb-expt.exe
*** WARNING: Unable to verify checksum for tbb.dll

DUMP_CLASS: 2

DUMP_QUALIFIER: 400

CONTEXT:  (.ecxr)
rax=0000000000000001 rbx=00000073bc0ff5e0 rcx=0000000000000007
rdx=000000000000000f rsi=00000073bc0fec50 rdi=00007ff7b3a24710
rip=00007ffb5997db8e rsp=00000073bc0fe410 rbp=00000073bc0fe570
 r8=000001f14807e5f0  r9=00007ffb59a31ec0 r10=0000000000000014
r11=000001f148080180 r12=00000073bc0fe690 r13=00000073bc0fee00
r14=00000000ffffffff r15=00000073bc0fee10
iopl=0         nv up ei pl nz na pe nc
cs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202
ucrtbase!abort+0x4e:
00007ffb`5997db8e cd29            int     29h
Resetting default scope

FAULTING_IP: 
ucrtbase!abort+4e
00007ffb`5997db8e cd29            int     29h

EXCEPTION_RECORD:  (.exr -1)
ExceptionAddress: 00007ffb5997db8e (ucrtbase!abort+0x000000000000004e)
   ExceptionCode: c0000409 (Security check failure or stack buffer overrun)
  ExceptionFlags: 00000001
NumberParameters: 1
   Parameter[0]: 0000000000000007
Subcode: 0x7 FAST_FAIL_FATAL_APP_EXIT


Here is clearly highlighted that .excr at line 15 has expection information. Lines 16-23 have register contents dumped. These are often helpful to make sense of what was being executed at the moment of error. With some basic assembly knowledge and patterns of code generated it can help pin-point the root cause in mins.

Line 24 points to the last stack-frame: ucrtbase!abort+0x4e:, the error was thrown from function abort in ucrtbase and offset 0x4e in that fn. The executed at that line is int 29h, which is an instruction defined by [__fastfail intrinsic] (https://docs.microsoft.com/en-us/cpp/intrinsics/fastfail?view=vs-2019)

Now it analyze extension tries to classify the error per MS classification system. Here it listed error as FATAL_APP_EXIT, however the error code 0xc0000409 specified is not really what happened with this application. This is a case of unhandled exception from a background thread, eventually crashing the process not a stack-based buffer overflow.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
BUGCHECK_STR:  FATAL_APP_EXIT

DEFAULT_BUCKET_ID:  FATAL_APP_EXIT

PROCESS_NAME:  tbb-expt.exe

ERROR_CODE: (NTSTATUS) 0xc0000409 - The system detected an overrun of a stack-based buffer in this application. This overrun could potentially allow a malicious user to gain control of this application.

EXCEPTION_CODE: (NTSTATUS) 0xc0000409 - The system detected an overrun of a stack-based buffer in this application. This overrun could potentially allow a malicious user to gain control of this application.

EXCEPTION_CODE_STR:  c0000409

EXCEPTION_PARAMETER1:  0000000000000007


I have pasted the entire output of this analyze -v command here on gist.github.com. Towards the end of analyze output it prints the call-stack for the thread from which the error was cached in .excr. However that is just 1 thread.

To further check what is going on, I am going to query threads in this dump with command ~:

Thread status command

0:009> ~
   0  Id: 62c.1084 Suspend: 1 Teb: 00000073`bacb7000 Unfrozen
   1  Id: 62c.3a48 Suspend: 1 Teb: 00000073`bacb9000 Unfrozen
   2  Id: 62c.2828 Suspend: 1 Teb: 00000073`bacbb000 Unfrozen
   3  Id: 62c.2850 Suspend: 1 Teb: 00000073`bacbd000 Unfrozen
   4  Id: 62c.2b28 Suspend: 1 Teb: 00000073`bacbf000 Unfrozen
   5  Id: 62c.41a0 Suspend: 1 Teb: 00000073`bacc1000 Unfrozen
   6  Id: 62c.40c Suspend: 0 Teb: 00000073`bacc3000 Unfrozen
   7  Id: 62c.854 Suspend: 0 Teb: 00000073`bacc5000 Unfrozen
   8  Id: 62c.2ef4 Suspend: 0 Teb: 00000073`bacc7000 Unfrozen
.  9  Id: 62c.2a10 Suspend: 0 Teb: 00000073`bacc9000 Unfrozen
  10  Id: 62c.1a58 Suspend: 0 Teb: 00000073`baccb000 Unfrozen
  11  Id: 62c.890 Suspend: 0 Teb: 00000073`baccd000 Unfrozen
  12  Id: 62c.17f0 Suspend: 0 Teb: 00000073`baccf000 Unfrozen

Here 62c is process-id and 1084, 3a48 and so on are thread-ids.

to check stack of thread 3a48 Thread#1, execute command: ~1 KB:

0:009> ~1 KB
 # RetAddr           : Args to Child                                                           : Call Site
00 00007ffb`5c9b4060 : 00000000`00000000 00000000`00000000 000001f1`480639d0 00000000`00000002 : ntdll!NtWaitForWorkViaWorkerFactory+0x14
01 00007ffb`5afc7bd4 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!TppWorkerThread+0x300
02 00007ffb`5c9eced1 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : kernel32!BaseThreadInitThunk+0x14
03 00000000`00000000 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!RtlUserThreadStart+0x21


To check stack of (upto 25 frames):

  • all threads, execute command: ~* KB 25
  • thread that caused exception: ~# K 25
  • ~# kP 25 provides with lot more information to browse thru

Note: K command takes various parameters and prints stack with various level of details. I have found kP to be quite verbose and browsable, but for blog posts KC option works best


Looking at call-stack:

0:009> ~# KC 25
 # Call Site
00 ucrtbase!abort
01 ucrtbase!terminate
02 VCRUNTIME140!FindHandler<__FrameHandler3>
03 VCRUNTIME140!__InternalCxxFrameHandler<__FrameHandler3>
04 VCRUNTIME140!__CxxFrameHandler3
05 ntdll!RtlpExecuteHandlerForException
06 ntdll!RtlDispatchException
07 ntdll!RtlRaiseException
08 KERNELBASE!RaiseException
09 VCRUNTIME140!_CxxThrowException
0a msvcp140!std::_Xout_of_range
0b tbb_expt!std::vector<int,std::allocator<int> >::_Xrange
0c tbb_expt!std::vector<int,std::allocator<int> >::at
0d tbb_expt!parallelFor::__l5::<lambda_66c7355d0edda6973d22642add3a91c2>::operator()
0e tbb_expt!std::_For_each_ivdep
0f tbb_expt!std::_Static_partitioned_for_each2<int *,__int64,<lambda_66c7355d0edda6973d22642add3a91c2> >::_Process_chunk
10 tbb_expt!std::_Run_available_chunked_work<std::_Static_partitioned_for_each2<int *,__int64,<lambda_66c7355d0edda6973d22642add3a91c2> > >
11 tbb_expt!std::_Static_partitioned_for_each2<int *,__int64,<lambda_66c7355d0edda6973d22642add3a91c2> >::_Threadpool_callback
12 ntdll!TppWorkpExecuteCallback
13 ntdll!TppWorkerThread
14 kernel32!BaseThreadInitThunk
15 ntdll!RtlUserThreadStart


we can safely say that std::vector<int>::at() threw an exception of for out of range: _Xout_of_range (Frame 0c and 0a). This raised an error _CxxThrowException at frame 09 and eventually lead to it trying to find exception handler to be executed: FindHandler<__FrameHandler3> at frame 02, which we did not register in first place: thus it called terminate.

The code is to this effect:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <iostream>
#include <vector>
#include <atomic>
#include <utility>
#include <algorithm>
#include <execution>

constexpr int StepSize = 2;
constexpr int Start = 0;
constexpr int End = 9;

static void parallelFor()
{
    std::vector<int> vec{0,1,2,3,4,5,6,7,8};
	vec.reserve(50);
	
    try
	{
	
	std::for_each(std::execution::par_unseq, vec.begin(), vec.end(),
			[&vec](int idx) -> void
			{
				std::cout << vec.at(idx * 3) << std::endl;			
			});
	}
	catch (std::exception& ex)
	{
        // this will never be called- as unhandled exception from background
        // thread will terminate application
	}
}

int main()
{
    parallelFor();
    return 0;
}


Now the funny thing is: even if you change the execution policy to std::execution::seq from std::execution::par_unseq, it will still crash the application. The rational behind this decision is to keep behavior consistent for API whether you are running parallel or not.

That’s it for now..