示例dump分析:重复释放同一堆内存
代码
1 2 3 4 5 6 7 8 9 10 11 |
int main() { init_breakpad(); int* p = new int(42); delete p; delete p; // 重复释放同一内存 std::cout << "Hello World!\n"; return 0; } |
分析步骤
- 用
windbg
打开Dump
文件并加载符号ctrl + s
ctrl + p
ctrl + i
- 自动分析崩溃原因
1 |
!analyze -v |
- 看到了相关异常信息如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
CONTEXT: (.ecxr) rax=00000000000080f3 rbx=0000000000000000 rcx=0000000000008123 rdx=00000000ffffffff rsi=0000000000000000 rdi=0000000000000000 rip=00007ff63e30fbee rsp=000000000014fbf0 rbp=000000000014fcb0 r8=00000000004d1500 r9=0000000000000001 r10=0000000000008000 r11=000000000014fa40 r12=0000000000000000 r13=0000000000000000 r14=0000000000000000 r15=0000000000000000 iopl=0 nv up ei pl nz na po nc cs=0033 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010206 aet_breakpad_test!_free_dbg+0x2e: 00007ff6`3e30fbee 8b401c mov eax,dword ptr [rax+1Ch] ds:00000000`0000810f=???????? Resetting default scope EXCEPTION_RECORD: (.exr -1) ExceptionAddress: 00007ff63e30fbee (aet_breakpad_test!_free_dbg+0x000000000000002e) ExceptionCode: c0000005 (Access violation) ExceptionFlags: 00000000 NumberParameters: 2 Parameter[0]: 0000000000000000 Parameter[1]: 000000000000810f Attempt to read from address 000000000000810f |
-
异常上下文解析:
- 可以看出,异常触发位置是:
aet_breakpad_test!_free_dbg+0x2e
说明崩溃发生在_free_dbg
(调试版堆释放函数)内部 - 异常代码
c0000005 (Access violation)
非法内存访问 - 奔溃发生的地址
000000000000810f
(这个就是无效地址)
总结来说就是在读取[rax+1Ch]
的时候发生了奔溃,80f3 + 1C = 810f
- 结论:
RAX
存储了一个无效的堆块地址,可能是由于:
堆块已被释放(双重释放)
堆块头信息被破坏(堆溢出或野指针写入)
- 可以看出,异常触发位置是:
-
查看调用栈
- 可以看到和我们自己的模块相关的代码是
main
这一行
- 可以看到和我们自己的模块相关的代码是
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
0:000> kn *** Stack trace for last set context - .thread/.cxr resets it # Child-SP RetAddr Call Site 00 00 00000000`0014fbf0 00007ff6`3e2907d8 aet_breakpad_test!_free_dbg+0x2e00000000`0014fbf0 00007ff6`3e2907d8 aet_breakpad_test!_free_dbg+0x2e [minkernel\crts\ucrt\src\appcrt\heap\debug_heap.cpp @ 1026] [minkernel\crts\ucrt\src\appcrt\heap\debug_heap.cpp @ 1026] 01 01 00000000`0014fc30 00007ff6`3e28ef18 aet_breakpad_test!operator delete+0x1800000000`0014fc30 00007ff6`3e28ef18 aet_breakpad_test!operator delete+0x18 [D:\a\_work\1\s\src\vctools\crt\vcstartup\src\heap\delete_scalar.cpp @ 34] [D:\a\_work\1\s\src\vctools\crt\vcstartup\src\heap\delete_scalar.cpp @ 34] 02 02 00000000`0014fc60 00007ff6`3e209762 aet_breakpad_test!operator delete+0x1800000000`0014fc60 00007ff6`3e209762 aet_breakpad_test!operator delete+0x18 [D:\a\_work\1\s\src\vctools\crt\vcstartup\src\heap\delete_scalar_size.cpp @ 31] [D:\a\_work\1\s\src\vctools\crt\vcstartup\src\heap\delete_scalar_size.cpp @ 31] 03 03 00000000`0014fc90 00007ff6`3e28f9a9 aet_breakpad_test!main+0xd200000000`0014fc90 00007ff6`3e28f9a9 aet_breakpad_test!main+0xd2 [Q:\google_code\breakpad\src\src\client\windows\aet_test\aet_breakpad_test\aet_breakpad_test.cpp @ 26] [Q:\google_code\breakpad\src\src\client\windows\aet_test\aet_breakpad_test\aet_breakpad_test.cpp @ 26] 04 04 00000000`0014fe10 00007ff6`3e28f852 aet_breakpad_test!invoke_main+0x3900000000`0014fe10 00007ff6`3e28f852 aet_breakpad_test!invoke_main+0x39 [D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 79] [D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 79] 05 05 00000000`0014fe60 00007ff6`3e28f70e aet_breakpad_test!__scrt_common_main_seh+0x13200000000`0014fe60 00007ff6`3e28f70e aet_breakpad_test!__scrt_common_main_seh+0x132 [D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 288] [D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 288] 06 06 00000000`0014fed0 00007ff6`3e28fa3e aet_breakpad_test!__scrt_common_main+0xe00000000`0014fed0 00007ff6`3e28fa3e aet_breakpad_test!__scrt_common_main+0xe [D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 331] [D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 331] 07 07 00000000`0014ff00 00007fff`188b7374 aet_breakpad_test!mainCRTStartup+0xe00000000`0014ff00 00007fff`188b7374 aet_breakpad_test!mainCRTStartup+0xe [D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_main.cpp @ 17] [D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_main.cpp @ 17] 08 08 00000000`0014ff30 00007fff`19ffcc91 kernel32!BaseThreadInitThunk+0x14 00000000`0014ff30 00007fff`19ffcc91 kernel32!BaseThreadInitThunk+0x14 09 09 00000000`0014ff60 00000000`00000000 ntdll!RtlUserThreadStart+0x21 00000000`0014ff60 00000000`00000000 ntdll!RtlUserThreadStart+0x21 |
- 检查变量和寄存器(储存制作
p
的值)
1 2 3 |
0:000> dx -r1 p p : 0x8123 : Unable to read memory at Address 0x8123 [Type: int *] Unable to read memory at Address 0x8123 |
- 反汇编
验证观察
- 附加到
exe
后,进行单步调试观察堆内存- 这一行代码执行后,用
!heap -p -a 0x00000000 0042bdd0
去观察,如下 - 状态是
busy
- 这一行代码执行后,用
1 |
int* p = new int(42); |
1 2 3 4 5 |
0:000> !heap -p -a 0x00000000`0042bdd0 address 000000000042bdd0 found in _HEAP @ 420000 HEAP_ENTRY Size Prev Flags UserPtr UserSize - state 000000000042bd90 0008 0000 [00] 000000000042bda0 00041 - (busy) |
- 第一次
delete
之后,再次观察- 发现状态是
free
了
- 发现状态是
1 2 3 4 5 6 |
0:000> !heap -p -a 0x00000000`0042bdd0 address 000000000042bdd0 found in _HEAP @ 420000 HEAP_ENTRY Size Prev Flags UserPtr UserSize - state 000000000042bd90 0008 0000 [00] 000000000042bda0 00070 - (free) |
- 继续往下走,观察第二次执行
delete
的变化- 发现崩溃了
- 再观察堆信息
总结
- 结合前面的异常代码和这里的空指针,可以认为是访问了未映射的地址
示例dump分析:死锁
代码
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 39 40 41 42 43 44 45 46 47 48 49 50 51 |
#include <Windows.h> #include <iostream> #include "client/windows/handler/exception_handler.h" CRITICAL_SECTION cs1, cs2; DWORD WINAPI ThreadA(LPVOID) { EnterCriticalSection(&cs1); Sleep(1000); EnterCriticalSection(&cs2); // 等待线程B释放cs1 LeaveCriticalSection(&cs2); LeaveCriticalSection(&cs1); return 0; } DWORD WINAPI ThreadB(LPVOID) { EnterCriticalSection(&cs2); Sleep(1000); EnterCriticalSection(&cs1); // 等待线程A释放cs2 LeaveCriticalSection(&cs1); LeaveCriticalSection(&cs2); return 0; } std::wstring get_dump_path() { std::wstring path = L"q:\\dump_repo\\"; return path; } google_breakpad::ExceptionHandler* g_breakpad_handle = nullptr; void init_breakpad() { g_breakpad_handle = new google_breakpad::ExceptionHandler( get_dump_path(), nullptr, nullptr, nullptr, google_breakpad::ExceptionHandler::HANDLER_ALL); } int main() { init_breakpad(); InitializeCriticalSection(&cs1); InitializeCriticalSection(&cs2); HANDLE hThreads[2]; hThreads[0] = CreateThread(NULL, 0, ThreadA, NULL, 0, NULL); hThreads[1] = CreateThread(NULL, 0, ThreadB, NULL, 0, NULL); WaitForMultipleObjects(2, hThreads, TRUE, INFINITE); std::cout << "Hello World!\n"; return 0; } |
分析步骤
- 由于这个是死锁,所以不是奔溃,所以要附加到进程来调试
- 自动分析崩溃原因
1 |
!analyze -v |
- 看到了相关异常信息如下
80000003
调试断点指令
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
EXCEPTION_RECORD: (.exr -1) ExceptionAddress: 00007fff1a051020 (ntdll!DbgBreakPoint) ExceptionCode: 80000003 (Break instruction exception) ExceptionFlags: 00000000 NumberParameters: 1 Parameter[0]: 0000000000000000 FAULTING_THREAD: 00004e10 PROCESS_NAME: aet_breakpad_test.exe ERROR_CODE: (NTSTATUS) 0x80000003 - { } EXCEPTION_CODE_STR: 80000003 EXCEPTION_PARAMETER1: 0000000000000000 |
- 列出所有线程
~*k
- 线程
0
,正在等待多个对象 - 线程
1
,是breakpad
的异常处理线程,处理等待单个对象的状态 - 线程
2
和3
,执行了ThreadA
和ThreadB
,并且都调用了RtlEnterCriticalSection
,但随后进入了等待状态
- 线程
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 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
0:004> ~*k 0 Id: 1548.28bc Suspend: 1 Teb: 00000000`003ac000 Unfrozen # Child-SP RetAddr Call Site 00 00000000`0014f998 00007fff`17803f30 ntdll!NtWaitForMultipleObjects+0x14 01 00000000`0014f9a0 00007fff`17803e2e KERNELBASE!WaitForMultipleObjectsEx+0xf0 02 00000000`0014fc90 00007ff7`ae0947a1 KERNELBASE!WaitForMultipleObjects+0xe 03 00000000`0014fcd0 00007ff7`ae11fb29 aet_breakpad_test!main+0xe1 [Q:\google_code\breakpad\src\src\client\windows\aet_test\aet_breakpad_test\aet_breakpad_test.cpp @ 48] 04 00000000`0014fe10 00007ff7`ae11f9d2 aet_breakpad_test!invoke_main+0x39 [D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 79] 05 00000000`0014fe60 00007ff7`ae11f88e aet_breakpad_test!__scrt_common_main_seh+0x132 [D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 288] 06 00000000`0014fed0 00007ff7`ae11fbbe aet_breakpad_test!__scrt_common_main+0xe [D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 331] 07 00000000`0014ff00 00007fff`188b7374 aet_breakpad_test!mainCRTStartup+0xe [D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_main.cpp @ 17] 08 00000000`0014ff30 00007fff`19ffcc91 KERNEL32!BaseThreadInitThunk+0x14 09 00000000`0014ff60 00000000`00000000 ntdll!RtlUserThreadStart+0x21 1 Id: 1548.6654 Suspend: 1 Teb: 00000000`003ae000 Unfrozen # Child-SP RetAddr Call Site 00 00000000`007bfe48 00007fff`177c920e ntdll!NtWaitForSingleObject+0x14 01 00000000`007bfe50 00007ff7`ae0987f2 KERNELBASE!WaitForSingleObjectEx+0x8e 02 00000000`007bfef0 00007fff`188b7374 aet_breakpad_test!google_breakpad::ExceptionHandler::ExceptionHandlerThreadMain+0xa2 [Q:\google_code\breakpad\src\src\client\windows\handler\exception_handler.cc @ 392] 03 00000000`007bff30 00007fff`19ffcc91 KERNEL32!BaseThreadInitThunk+0x14 04 00000000`007bff60 00000000`00000000 ntdll!RtlUserThreadStart+0x21 2 Id: 1548.258c Suspend: 1 Teb: 00000000`003b2000 Unfrozen # Child-SP RetAddr Call Site 00 00000000`009bfc18 00007fff`1a014d6d ntdll!NtWaitForAlertByThreadId+0x14 01 00000000`009bfc20 00007fff`1a014c22 ntdll!RtlpWaitOnAddressWithTimeout+0x81 02 00000000`009bfc50 00007fff`1a014a3d ntdll!RtlpWaitOnAddress+0xae 03 00000000`009bfcc0 00007fff`19fdfcb4 ntdll!RtlpWaitOnCriticalSection+0xfd 04 00000000`009bfda0 00007fff`19fdfae2 ntdll!RtlpEnterCriticalSectionContended+0x1c4 05 00000000`009bfe00 00007ff7`ae090eb7 ntdll!RtlEnterCriticalSection+0x42 06 00000000`009bfe30 00007fff`188b7374 aet_breakpad_test!ThreadA+0x47 [Q:\google_code\breakpad\src\src\client\windows\aet_test\aet_breakpad_test\aet_breakpad_test.cpp @ 13] 07 00000000`009bff30 00007fff`19ffcc91 KERNEL32!BaseThreadInitThunk+0x14 08 00000000`009bff60 00000000`00000000 ntdll!RtlUserThreadStart+0x21 3 Id: 1548.5a74 Suspend: 1 Teb: 00000000`003b4000 Unfrozen # Child-SP RetAddr Call Site 00 00000000`00abfc18 00007fff`1a014d6d ntdll!NtWaitForAlertByThreadId+0x14 01 00000000`00abfc20 00007fff`1a014c22 ntdll!RtlpWaitOnAddressWithTimeout+0x81 02 00000000`00abfc50 00007fff`1a014a3d ntdll!RtlpWaitOnAddress+0xae 03 00000000`00abfcc0 00007fff`19fdfcb4 ntdll!RtlpWaitOnCriticalSection+0xfd 04 00000000`00abfda0 00007fff`19fdfae2 ntdll!RtlpEnterCriticalSectionContended+0x1c4 05 00000000`00abfe00 00007ff7`ae090f47 ntdll!RtlEnterCriticalSection+0x42 06 00000000`00abfe30 00007fff`188b7374 aet_breakpad_test!ThreadB+0x47 [Q:\google_code\breakpad\src\src\client\windows\aet_test\aet_breakpad_test\aet_breakpad_test.cpp @ 21] 07 00000000`00abff30 00007fff`19ffcc91 KERNEL32!BaseThreadInitThunk+0x14 08 00000000`00abff60 00000000`00000000 ntdll!RtlUserThreadStart+0x21 # 4 Id: 1548.4e10 Suspend: 1 Teb: 00000000`003b6000 Unfrozen # Child-SP RetAddr Call Site 00 00000000`008bfef8 00007fff`1a07ca7e ntdll!DbgBreakPoint 01 00000000`008bff00 00007fff`188b7374 ntdll!DbgUiRemoteBreakin+0x4e 02 00000000`008bff30 00007fff`19ffcc91 KERNEL32!BaseThreadInitThunk+0x14 03 00000000`008bff60 00000000`00000000 ntdll!RtlUserThreadStart+0x21 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
0:004> ~2k # Child-SP RetAddr Call Site 00 00000000`009bfc18 00007fff`1a014d6d ntdll!NtWaitForAlertByThreadId+0x14 01 00000000`009bfc20 00007fff`1a014c22 ntdll!RtlpWaitOnAddressWithTimeout+0x81 02 00000000`009bfc50 00007fff`1a014a3d ntdll!RtlpWaitOnAddress+0xae 03 00000000`009bfcc0 00007fff`19fdfcb4 ntdll!RtlpWaitOnCriticalSection+0xfd 04 00000000`009bfda0 00007fff`19fdfae2 ntdll!RtlpEnterCriticalSectionContended+0x1c4 05 00000000`009bfe00 00007ff7`ae090eb7 ntdll!RtlEnterCriticalSection+0x42 06 00000000`009bfe30 00007fff`188b7374 aet_breakpad_test!ThreadA+0x47 [Q:\google_code\breakpad\src\src\client\windows\aet_test\aet_breakpad_test\aet_breakpad_test.cpp @ 13] 07 00000000`009bff30 00007fff`19ffcc91 KERNEL32!BaseThreadInitThunk+0x14 08 00000000`009bff60 00000000`00000000 ntdll!RtlUserThreadStart+0x21 0:004> ~3k # Child-SP RetAddr Call Site 00 00000000`00abfc18 00007fff`1a014d6d ntdll!NtWaitForAlertByThreadId+0x14 01 00000000`00abfc20 00007fff`1a014c22 ntdll!RtlpWaitOnAddressWithTimeout+0x81 02 00000000`00abfc50 00007fff`1a014a3d ntdll!RtlpWaitOnAddress+0xae 03 00000000`00abfcc0 00007fff`19fdfcb4 ntdll!RtlpWaitOnCriticalSection+0xfd 04 00000000`00abfda0 00007fff`19fdfae2 ntdll!RtlpEnterCriticalSectionContended+0x1c4 05 00000000`00abfe00 00007ff7`ae090f47 ntdll!RtlEnterCriticalSection+0x42 06 00000000`00abfe30 00007fff`188b7374 aet_breakpad_test!ThreadB+0x47 [Q:\google_code\breakpad\src\src\client\windows\aet_test\aet_breakpad_test\aet_breakpad_test.cpp @ 21] 07 00000000`00abff30 00007fff`19ffcc91 KERNEL32!BaseThreadInitThunk+0x14 08 00000000`00abff60 00000000`00000000 ntdll!RtlUserThreadStart+0x21 |
- 用
!locks
查看
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
0:004> !locks *** WARNING: Unable to verify checksum for Q:\google_code\breakpad\src\src\client\windows\x64\Debug\aet_breakpad_test.exe CritSec aet_breakpad_test!cs2+0 at 00007ff7ae27f8f8 WaiterWoken No LockCount 1 RecursionCount 1 OwningThread 5a74 EntryCount 0 ContentionCount 1 *** Locked CritSec aet_breakpad_test!cs1+0 at 00007ff7ae27f8d0 WaiterWoken No LockCount 1 RecursionCount 1 OwningThread 258c EntryCount 0 ContentionCount 1 *** Locked Scanned 5 critical sections |
1 2 3 4 |
CritSec aet_breakpad_test!cs1+0 at 00007ff7ae27f8d0 OwningThread 258c // 被线程 258c 持有 LockCount 1 // 表示有线程在等待此锁 *** Locked |
1 2 3 4 |
CritSec aet_breakpad_test!cs2+0 at 00007ff7ae27f8f8 OwningThread 5a74 // 被线程 5a74 持有 LockCount 1 // 表示有线程在等待此锁 *** Locked |
- 在调用栈中显示,线程
258c
是ThreadA
,当前正 等待获取cs2
:
1 |
aet_breakpad_test!ThreadA+0x47 [Q:\...\aet_breakpad_test.cpp @ 13] |
1 |
EnterCriticalSection(&cs2); // 尝试获取 cs2 |
- 在调用栈中显示,线程
5a74
是ThreadB
,当前正 等待获取cs1
:
1 |
aet_breakpad_test!ThreadB+0x47 [Q:\...\aet_breakpad_test.cpp @ 21] |
1 |
EnterCriticalSection(&cs1); // 尝试获取 cs1 |
验证观察
- 查看
cs1
的相关信息
1 2 3 4 5 6 7 8 9 10 11 12 |
0:004> !cs -s 00007ff7ae27f8d0 ----------------------------------------- Critical section = 0x00007ff7ae27f8d0 (aet_breakpad_test!cs1+0x0) DebugInfo = 0x00007fff1a11af00 LOCKED LockCount = 0x1 WaiterWoken = No OwningThread = 0x000000000000258c RecursionCount = 0x1 LockSemaphore = 0xFFFFFFFF SpinCount = 0x00000000020007d0 ntdll!RtlpStackTraceDataBase is NULL. Probably the stack traces are not enabled. |
字段 | 值/含义 |
Critical section | 0x00007ff7ae27f8d0 (临界区 cs1 的地址) |
LockCount | 0x1 (有 1 个线程 在等待此临界区) |
OwningThread | 0x258c (持有此临界区的线程 ID 是 258c ,对应线程 2) |
RecursionCount | 0x1 (持有线程已 重入锁 1 次,即首次获取后未释放) |
WaiterWoken | No (无等待线程被唤醒) |
SpinCount | 0x20007d0 (自旋计数,用于优化多核环境下的锁竞争) |
StackTraceDataBase | NULL (堆栈跟踪未启用,需配置符号或启用调试选项) |
- 查看
cs2
的相关信息
1 2 3 4 5 6 7 8 9 10 11 12 |
0:004> !cs -s 00007ff7ae27f8f8 ----------------------------------------- Critical section = 0x00007ff7ae27f8f8 (aet_breakpad_test!cs2+0x0) DebugInfo = 0x00007fff1a11aed0 LOCKED LockCount = 0x1 WaiterWoken = No OwningThread = 0x0000000000005a74 RecursionCount = 0x1 LockSemaphore = 0xFFFFFFFF SpinCount = 0x00000000020007d0 ntdll!RtlpStackTraceDataBase is NULL. Probably the stack traces are not enabled. |
总结
ThreadA
持有cs1
→ 等待cs2
ThreadB
持有cs2
→ 等待cs1
本文为原创文章,版权归Aet所有,欢迎分享本文,转载请保留出处!
你可能也喜欢
- ♥ Soui一03/17
- ♥ Windows调试相关简记12/13
- ♥ COM组件_303/07
- ♥ breakpad记述:Windows下静态库的编译使用03/15
- ♥ Dump分析:空指针访问二,重复释放堆内存二03/30
- ♥ COM组件_207/22