当前位置: 首页 > news >正文

堆溢出崩溃分析Critical error detected c0000374

(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)

相关文章:

  • C++中内存泄漏,内存溢出区别及检查方法
  • [win10] ffmpeg gpu加速
  • Qt/null 空指针的一些操作规则
  • FFmpeg H264编码
  • 图像锐化是什么
  • FFmpeg原始帧处理-滤镜API用法详解
  • ffmpeg中的rtbufsize
  • 智能指针的实现
  • 什么是构造函数和析构函数?
  • CD-ROM
  • 光盘文件格式-udf、iso9660、Joliet、Romeo
  • 刻录光盘的程序步骤
  • ISO文件
  • DVD-数字通用光盘
  • VCD-影音光碟
  • 【附node操作实例】redis简明入门系列—字符串类型
  • 【跃迁之路】【444天】程序员高效学习方法论探索系列(实验阶段201-2018.04.25)...
  • Android Volley源码解析
  • Brief introduction of how to 'Call, Apply and Bind'
  • exif信息对照
  • Git学习与使用心得(1)—— 初始化
  • HTTP中GET与POST的区别 99%的错误认识
  • java2019面试题北京
  • JavaScript 是如何工作的:WebRTC 和对等网络的机制!
  • Java编程基础24——递归练习
  • Java多线程(4):使用线程池执行定时任务
  • leetcode378. Kth Smallest Element in a Sorted Matrix
  • Making An Indicator With Pure CSS
  • vue-router的history模式发布配置
  • 对象引论
  • 将回调地狱按在地上摩擦的Promise
  • 算法系列——算法入门之递归分而治之思想的实现
  • 通过git安装npm私有模块
  • 详解NodeJs流之一
  • 新书推荐|Windows黑客编程技术详解
  • Hibernate主键生成策略及选择
  • ​二进制运算符:(与运算)、|(或运算)、~(取反运算)、^(异或运算)、位移运算符​
  • # 日期待t_最值得等的SUV奥迪Q9:空间比MPV还大,或搭4.0T,香
  • # 再次尝试 连接失败_无线WiFi无法连接到网络怎么办【解决方法】
  • #android不同版本废弃api,新api。
  • #Linux杂记--将Python3的源码编译为.so文件方法与Linux环境下的交叉编译方法
  • #绘制圆心_R语言——绘制一个诚意满满的圆 祝你2021圆圆满满
  • $ git push -u origin master 推送到远程库出错
  • $forceUpdate()函数
  • ()、[]、{}、(())、[[]]命令替换
  • (003)SlickEdit Unity的补全
  • (2)Java 简介
  • (八)Flask之app.route装饰器函数的参数
  • (附源码)springboot 房产中介系统 毕业设计 312341
  • (七)理解angular中的module和injector,即依赖注入
  • (转)Sublime Text3配置Lua运行环境
  • ***原理与防范
  • .net 桌面开发 运行一阵子就自动关闭_聊城旋转门家用价格大约是多少,全自动旋转门,期待合作...
  • .netcore 6.0/7.0项目迁移至.netcore 8.0 注意事项
  • .NET中winform传递参数至Url并获得返回值或文件