堆溢出崩溃分析Critical error detected c0000374

68 篇文章 0 订阅

(Owed by: 春夜喜雨 http://blog.csdn.net/chunyexiyu)
参考:https://blog.csdn.net/q610098308/article/details/94630682
参考:https://blog.csdn.net/weitaming1/article/details/84023789
参考:https://stackoverflow.com/questions/23471161/critical-error-detected-c0000374-c-dll-returns-pointer-off-allocated-memory

下面通过触发异常,崩溃堆栈分析,来看看异常发生的场景,以及如何来界定问题范围。

1. 触发崩溃

首先触发异常发生,先写一段会崩溃的函数,在main中进行调用
int* test_heap_alloc()
{
int* pTable = new int(256); // 申请一个int结构体变量,初始值为256;
for (int i = 0; i < 256; i++)
pTable[i] = i;
return pTable;
}
int main(int argc, char** argv){
test_heap_alloc();
return 0;
}

执行后,在vs输出错误信息:
Critical error detected c0000374

2. 问题分析

引用一些来自stackoverflow中Freya301的错误分析思路:
通常错误来源于(malloc/new/其它内存申请)检测到了堆异常;
通常异常已经发生在之前的程序里;
但是崩溃抛出延迟了,直到下次堆申请时才发生。
sometimes its because malloc/new/whatever detects heap corruption,
often this corruption has already occurred previously in the program,
but the crash has been delayed until the next call to new/malloc.

从这个崩溃来看,符合这个思路:

  1. 申请堆内存容量4字节,但使用时超出4字节使用,写入到了未申请的空间中;
  2. 下次申请/释放堆内存时会触发崩溃
  3. 如果没有再申请/释放,但此时程序结束,则在程序结束检查释放时发生了崩溃;
3. 下次申请时触发的堆检测崩溃

在test_heap_malloc中使用超申请堆内存,再次申请内存,崩溃在new char()位置上
int main(int argc, char** argv){
test_heap_alloc();
char* p = new char(0); // 申请一个字节,内容初始化为0
return 0;
}

汇编代码栈:
00007FFB8ED4F1E0 je RtlReportCriticalFailure+65h (07FFB8ED4F1F1h)
00007FFB8ED57F9D call RtlpReportHeapFailure (07FFB8ED5ACF8h)
00007FFB8ED58285 call RtlpHeapHandleError (07FFB8ED57F90h)
00007FFB8ED5DF0C call RtlpHpHeapHandleError (07FFB8ED58210h)
00007FFB8EC7D761 call RtlpLogHeapFailure (07FFB8ED5DECCh)
00007FFB8EC7B448 call RtlpAllocateHeap (07FFB8EC7D160h) —ntdll.dll
00007FFB8CA0FDE0 call qword ptr [__imp_HeapAlloc (07FFB8CAB93C0h)] —ucrtbase.dll
00007FF72007119E call malloc (07FF720071F84h)
00007FF720071130 call operator new (07FF720071184h) --test.exe
崩溃堆栈:
ntdll.dll!RtlReportCriticalFailure()
ntdll.dll!RtlpHeapHandleError()
ntdll.dll!RtlpHpHeapHandleError()
ntdll.dll!RtlpLogHeapFailure()
ntdll.dll!RtlpAllocateHeap()
ntdll.dll!RtlpAllocateHeapInternal()
ucrtbase.dll!_malloc_base()
test.exe!operator new(unsigned __int64 size) 行 35 C++
test.exe!main(int argc, char * * argv) 行 93 C++
[外部代码]

4. 下次释放时触发的堆检测崩溃

修改main代码,则崩溃发生在delete释放堆内存时
int main(int argc, char** argv){
int* p = test_heap_alloc();
delete p;
return 0;
}

汇编代码栈:
00007FFB8ED4F1E0 je RtlReportCriticalFailure+65h (07FFB8ED4F1F1h)
00007FFB8ED57F9D call RtlpReportHeapFailure (07FFB8ED5ACF8h)
00007FFB8ED58285 call RtlpHeapHandleError (07FFB8ED57F90h)
00007FFB8ED5DF0C call RtlpHpHeapHandleError (07FFB8ED58210h)
00007FFB8EC760E0 call RtlpLogHeapFailure (07FFB8ED5DECCh)
00007FFB8EC75B6F call RtlpFreeHeap (07FFB8EC75C00h)
00007FFB8EC747AC call RtlpFreeHeapInternal (07FFB8EC75710h) —ntdll.dll
00007FFB8CA0F055 call qword ptr [__imp_HeapFree (07FFB8CAB9398h)] —ucrtbase.dll
00007FF7515110BD call operator delete (07FF751511140h) --test.exe
崩溃堆栈:
ntdll.dll!RtlReportCriticalFailure()
ntdll.dll!RtlpHeapHandleError()
ntdll.dll!RtlpHpHeapHandleError()
ntdll.dll!RtlpLogHeapFailure()
ntdll.dll!RtlpFreeHeap()
ntdll.dll!RtlpFreeHeapInternal()
ntdll.dll!RtlFreeHeap()
ucrtbase.dll!_free_base()
demo_snappy_test.exe!main(int argc, char * * argv) 行 95 C++
[外部代码]

5. 程序结束时触发的堆检测崩溃

程序结束崩溃发生在代码:
文件exec_common.inl,exit(main_result)行
if (!__scrt_is_managed_app())
exit(main_result);
汇编代码栈:
00007FFB8ED4F1E0 je RtlReportCriticalFailure+65h (07FFB8ED4F1F1h)
00007FFB8ED57F9D call RtlpReportHeapFailure (07FFB8ED5ACF8h)
00007FFB8ED58285 call RtlpHeapHandleError (07FFB8ED57F90h)
00007FFB8ED5DF0C call RtlpHpHeapHandleError (07FFB8ED58210h)
00007FFB8EC767D2 call RtlpLogHeapFailure (07FFB8ED5DECCh)
00007FFB8EC75B6F call RtlpFreeHeap (07FFB8EC75C00h)
00007FFB8EC747AC call RtlpFreeHeapInternal (07FFB8EC75710h) — ntdll.dll
00007FFB8CA0F055 call qword ptr [__imp_HeapFree (07FFB8CAB9398h)]
00007FFB8CA1432B call _free_base (07FFB8CA0F040h)
00007FFB8CA141F6 call <lambda_f03950bc5685219e0bcd2087efbe011e>::operator() (07FFB8CA14230h)
00007FFB8CA141AF call __crt_seh_guarded_call::operator()<<lambda_7777bce6b2f8c936911f934f8298dc43>,<lambda_f03950bc5685219e0bcd2087efbe011e> &,<lambda_3883c3dff614d5e0c5f61bb1ac94921c> > (07FFB8CA141C0h)
00007FFB8CA2051D call _execute_onexit_table (07FFB8CA14180h)
00007FFB8CA204A6 call <lambda_ad52fe89635f51ec3b38e9c3ac6dac81>::operator() (07FFB8CA204D4h)
00007FFB8CA20449 call __crt_seh_guarded_call::operator()<<lambda_123965863b7b46a3332720573f9ce793>,<lambda_ad52fe89635f51ec3b38e9c3ac6dac81> &,<lambda_8d528b66de6ae1e796d7f5e3101fca72> > (07FFB8CA20470h) — ucrtbase.dll
00007FF688B8140A call exit (07FF688B81FAAh) — test.exe
00007FFB8DC6702E call qword ptr [__guard_dispatch_icall_fptr (07FFB8DCD3220h)] —kernel32.dll
00007FFB8ECA264B call qword ptr [__guard_dispatch_icall_fptr (07FFB8EDD3000h)] —ntdll.dll
崩溃堆栈:
ntdll.dll!RtlReportCriticalFailure()
ntdll.dll!RtlpHeapHandleError()
ntdll.dll!RtlpHpHeapHandleError()
ntdll.dll!RtlpLogHeapFailure()
ntdll.dll!RtlpFreeHeap()
ntdll.dll!RtlpFreeHeapInternal()
ntdll.dll!RtlFreeHeap()
ucrtbase.dll!_free_base()
ucrtbase.dll!(void)()
ucrtbase.dll!__crt_seh_guarded_call::operator()<<lambda_7777bce6b2f8c936911f934f8298dc43>,(void) &,<lambda_3883c3dff614d5e0c5f61bb1ac94921c> >()
ucrtbase.dll!_execute_onexit_table()
ucrtbase.dll!(void)()
ucrtbase.dll!__crt_seh_guarded_call::operator()<<lambda_123965863b7b46a3332720573f9ce793>,(void) &,<lambda_8d528b66de6ae1e796d7f5e3101fca72> >()
ucrtbase.dll!common_exit()
test.exe!__scrt_common_main_seh() 行 295 C++
kernel32.dll!BaseThreadInitThunk()
ntdll.dll!RtlUserThreadStart()

6. 小结

a. 借助于这几个崩溃的汇编堆栈,可以看出来,存在堆溢出检查:

  • 在申请堆内存时,存在检查;
  • 在释放堆内存时,存在检查;
  • 在程序结束的时候,也会触发堆内存释放,存在检查;
    而检查点将会是一个识别点,在此时可能异常抛出,并触发了崩溃;

b. 检查点并不是发生堆内存错误使用的点,错误使用点在检查之前发生的;

c. 借助于申请/释放的堆检查特点,我们同样可以用于界定问题的代码行:
例如可以通过在代码中添加内存申请来检测异常,例如添加多处new char(),来检测堆使用异常,在代码中识别异常的所在代码行;

(Owed by: 春夜喜雨 http://blog.csdn.net/chunyexiyu)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

春夜喜雨

稀罕你的喜欢!!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值