在开发中遇到用户的反馈的崩溃,显示的栈溢出导致,通过查看资料终于找到了相关的发生问题原因。本文会介绍

1、栈溢出的排查过程,可能引起栈溢出原因

2、遇到栈不正确情况的处理过程

3、运气也是调试的一部分,哈哈

问题描述

程序崩溃,调试显示栈溢出

c0000409 (Security check failure or stack buffer overrun)

系统在此应用程序中检测到基于堆栈的缓冲区溢出。此超限可能会允许恶意用户控制此应用程序。

EXCEPTION_RECORD:  (.exr -1)
ExceptionAddress: 08f276bd (nvoglv32!vk_optimusGetInstanceProcAddr+0x000c549d)ExceptionCode: c0000409 (Security check failure or stack buffer overrun)ExceptionFlags: 00000001
NumberParameters: 1Parameter[0]: 00000007EXCEPTION_RECORD:  (.exr -1)
ExceptionAddress: 08f276bd (nvoglv32!vk_optimusGetInstanceProcAddr+0x000c549d)ExceptionCode: c0000409 (Security check failure or stack buffer overrun)ExceptionFlags: 00000001
NumberParameters: 1Parameter[0]: 00000007
Subcode: 0x7 FAST_FAIL_FATAL_APP_EXIT PROCESS_NAME:  lec_teacher.exeERROR_CODE: (NTSTATUS) 0xc0000409 - <Unable to get error code text>EXCEPTION_CODE_STR:  c0000409EXCEPTION_PARAMETER1:  00000007STACK_TEXT:
WARNING: Stack unwind information not available. Following frames may be wrong.
0813ec98 08ba784f     6e760038 00000000 00000000 nvoglv32!vk_optimusGetInstanceProcAddr+0xc549d
0813ecec 08ba79b0     0c870048 0c870048 00000000 nvoglv32!DrvPresentBuffers+0x2971f
0813ed34 08c830d8     0c870048 00000045 0c870048 nvoglv32!DrvPresentBuffers+0x29880
0813ed50 08c8853a     0000ffff 0c870048 0000005b nvoglv32!DrvPresentBuffers+0x104fa8
0813ed70 08c7739d     0c870048 00000045 0c870048 nvoglv32!DrvPresentBuffers+0x10a40a
0813f030 08c77dbc     01870048 0813fba0 0813f200 nvoglv32!DrvPresentBuffers+0xf926d
0813f1bc 081f85f2     0013f601 0c870048 0813f6a4 nvoglv32!DrvPresentBuffers+0xf9c8c
0813f218 08b56073     00000001 00000001 00000001 nvoglv32+0xb85f2
0813f248 08c5b992     16eac138 0813f6a4 0c870048 nvoglv32!DrvValidateVersion+0x5e93
0813f688 0820ef6d     0c870048 0813f6a4 00000002 nvoglv32!DrvPresentBuffers+0xdd862
0813fdfc 08930061     01000000 00000000 00000000 nvoglv32+0xcef6d
0813fe3c 0830d1d2     00000000 00000000 00000140 nvoglv32+0x7f0061
0813fe64 088fe217     00000000 00000000 00000140 nvoglv32+0x1cd1d2
0813fea8 089ea0fb     0a3b1040 00000000 00000d44 nvoglv32+0x7be217
0813fee0 089e9e2f     0a3b1040 08b5b100 08b5b100 nvoglv32+0x8aa0fb
0813fef8 08b5b122     0a867540 07e40080 0813ff18 nvoglv32+0x8a9e2f
0813ff08 75d26359     0a867540 75d26340 0813ff74 nvoglv32!DrvValidateVersion+0xaf42
0813ff18 778a7c24     0a867540 59857cfe 00000000 kernel32!BaseThreadInitThunk+0x19
0813ff74 778a7bf4     ffffffff 778c8ff3 00000000 ntdll!__RtlUserThreadStart+0x2f
0813ff84 00000000     08b5b100 0a867540 00000000 ntdll!_RtlUserThreadStart+0x1b

如何调试栈溢出

引起调用栈溢出错误

堆栈溢出是用户模式线程可能遇到的错误。有三个可能的原因导致此错误:

  • 线程使用为其保留的整个堆栈。这通常是由无限递归引起的。

  • 线程无法扩展堆栈,因为页面文件已用完,因此无法提交任何其他页面来扩展堆栈。

  • 线程无法扩展堆栈,因为系统在用于扩展页面文件的短暂时间内。

排查过程

1、~* 显示崩溃线程,线程号前面的 . 表示当前的线程,#表示异常线程,然后切换到崩溃线程 ~1s ,切换到1号线程。

0:002> ~*0  Id: 1658.c64 Suspend: 1 Teb: 00d5d000 Unfrozen ""Start: lec_teacher!WinMainCRTStartup (00367c52)Priority: 0  Priority class: 32  Affinity: ff
#  1  Id: 1658.d44 Suspend: 0 Teb: 00d78000 UnfrozenStart: nvoglv32!DrvValidateVersion+0xaf20 (08b5b100)Priority: 0  Priority class: 32  Affinity: ff
.  2  Id: 1658.213c Suspend: 1 Teb: 00d7b000 UnfrozenStart: nvoglv32!vk_optimusGetInstanceProcAddr+0xc6b3f (08f28d5f)0:002> ~1s
eax=00000001 ebx=00000000 ecx=00000007 edx=000001e3 esi=10e8b840 edi=00000007
eip=08f276bd esp=0813e884 ebp=0813ec98 iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202
nvoglv32!vk_optimusGetInstanceProcAddr+0xc549d:
08f276bd cd29            int     29h

2、查看TEB (Thread Environment Block),如果已经具备pdb的情况下可以直接使用!teb,如果没有符号表,可以展示内存进行映射 dd 00d78000 L4

0:001> !teb
TEB at 00d78000ExceptionList:        0813ff64StackBase:            08140000StackLimit:           0812d000SubSystemTib:         00000000FiberData:            00001e00ArbitraryUserPointer: 00000000Self:                 00d78000EnvironmentPointer:   00000000ClientId:             00001658 . 00000d44RpcHandle:            00000000Tls Storage:          355531b8PEB Address:          00d5a000LastErrorValue:       203LastStatusValue:      c0000100Count Owned Locks:    0HardErrorMode:        00:001> dd 00d78000 L4
00d78000  0813ff64 08140000 0812d000 00000000

3、要对此进行解释,您需要查找TEB数据结构的定义,网上可以查一下就可以找到。

typedef struct _TEB {NT_TIB NtTib;PVOID  EnvironmentPointer;CLIENT_ID ClientId;PVOID ActiveRpcHandle;PVOID ThreadLocalStoragePointer;PPEB ProcessEnvironmentBlock;ULONG LastErrorValue;.....PVOID DeallocationStack;.....
} TEB;typedef struct _NT_TIB {struct _EXCEPTION_REGISTRATION_RECORD *ExceptionList;PVOID StackBase;PVOID StackLimit;.....
} NT_TIB; 

这表明TEB结构中的第二个和第三个DWORD分别指向堆栈的底部和顶部。在这种情况下,这些地址是0x08140000 和0x0812d000。(堆栈在内存中向下增长。)您可以使用计算堆栈大小

0:001> ? 08140000 - 0812d000
Evaluate expression: 77824 = 00013000

这表明堆栈大小为76K。最大堆栈大小存储在字段DeallocationStack中。经过一些计算,确定该字段的偏移量是0xE0C。如果有符号表,可以使用使用dt teb 完成上面的计算,直接可以找到偏移量

查看最大的栈大小是1048576 = 1M,所以最大的栈大小是1M ,这意味着剩余了足够的堆栈空间。

0:001> dd 00d78000+e0c L1
00d78e0c  080400000:001> ? 08140000 - 08040000
Evaluate expression: 1048576 = 00100000

此外,此过程看起来很干净-通过使用基于堆栈的数据结构过大,它不会无限递归或超出其堆栈空间。

4、现在进入KD,并使用!vm extension命令查看整个系统的内存使用情况:但是需要设置权限才可以

0:002> .breakin
Break instruction exception - code 80000003 (first chance)
ntoskrnl!_DbgBreakPointWithStatus+4:
80148f9c cc               int     3kd> !vm *** Virtual Memory Usage ***Physical Memory:     16268   (   65072 Kb)Page File: \??\C:\pagefile.sysCurrent:    147456Kb Free Space:     65988KbMinimum:     98304Kb Maximum:       196608KbAvailable Pages:      2299   (    9196 Kb)ResAvail Pages:       4579   (   18316 Kb)Locked IO Pages:        93   (     372 Kb)Free System PTEs:    42754   (  171016 Kb)Free NP PTEs:         5402   (   21608 Kb)Free Special NP:       348   (    1392 Kb)Modified Pages:        757   (    3028 Kb)NonPagedPool Usage:    811   (    3244 Kb)NonPagedPool Max:     6252   (   25008 Kb)PagedPool 0 Usage:    1337   (    5348 Kb)PagedPool 1 Usage:     893   (    3572 Kb)PagedPool 2 Usage:     362   (    1448 Kb)PagedPool Usage:      2592   (   10368 Kb)PagedPool Maximum:   13312   (   53248 Kb)Shared Commit:        3928   (   15712 Kb)Special Pool:         1040   (    4160 Kb)Shared Process:       3641   (   14564 Kb)PagedPool Commit:     2592   (   10368 Kb)Driver Commit:         887   (    3548 Kb)Committed pages:     45882   (  183528 Kb)Commit limit:        50570   (  202280 Kb)Total Private:       33309   (  133236 Kb).....

首先,查看非分页和分页池的使用情况。两者都在限制范围内,因此这不是问题的原因。

接下来,查看已提交的页面数:202280中的183528。这非常接近限制。尽管此显示未显示此数字是完全处于极限,但是请记住,在执行用户模式调试时,系统上正在运行其他进程。每次执行NTSD命令时,这些其他进程也在分配和释放内存。这意味着您不完全了解堆栈溢出发生时的内存状态。鉴于已提交的页数与限制的接近程度,可以合理地得出结论,该页面文件在某个时刻已用完,这会导致堆栈溢出。

这并非罕见,而且目标应用程序也不会因此而真正出错。如果经常发生,您可能要考虑提高失败应用程序的初始堆栈承诺。这种情况概率非常小。

5、查看最后函数最后一次申请的栈空间大小

函数开始都是保存基地址,将esp 作为 ebp,下一条指令就是当前局部变量需要的申请的空间,sub esp, 10h ,这个表示的意思就局部变量占用16字节的大小,由于栈是从高地址向低地址,所以是sub。

最后申请的空间也不大,16字节

08f27608 55             push    ebp
08f27609 8bec           mov     ebp, esp
08f2760b 83ec10         sub     esp, 10h

通过上面可以基本排查出引起调用栈溢出的错误原因

调用栈不正确

还存在一种调用栈不正确的情况,需要找出的正确的调用栈,才能判断是否存在问题

WARNING: Stack unwind information not available. Following frames may be wrong.

此警告表明调试器不确定此消息之后列出的调用堆栈中的帧是否正确。

0:001> kb*** Stack trace for last set context - .thread/.cxr resets it# ChildEBP RetAddr      Args to Child
WARNING: Stack unwind information not available. Following frames may be wrong.
00 0813ec98 08ba784f     6e760038 00000000 00000000 nvoglv32!vk_optimusGetInstanceProcAddr+0xc549d
01 0813ecec 08ba79b0     0c870048 0c870048 00000000 nvoglv32!DrvPresentBuffers+0x2971f
02 0813ed34 08c830d8     0c870048 00000045 0c870048 nvoglv32!DrvPresentBuffers+0x29880
03 0813ed50 08c8853a     0000ffff 0c870048 0000005b nvoglv32!DrvPresentBuffers+0x104fa8
04 0813ed70 08c7739d     0c870048 00000045 0c870048 nvoglv32!DrvPresentBuffers+0x10a40a
05 0813f030 08c77dbc     01870048 0813fba0 0813f200 nvoglv32!DrvPresentBuffers+0xf926d
06 0813f1bc 081f85f2     0013f601 0c870048 0813f6a4 nvoglv32!DrvPresentBuffers+0xf9c8c
07 0813f218 08b56073     00000001 00000001 00000001 nvoglv32+0xb85f2
08 0813f248 08c5b992     16eac138 0813f6a4 0c870048 nvoglv32!DrvValidateVersion+0x5e93
09 0813f688 0820ef6d     0c870048 0813f6a4 00000002 nvoglv32!DrvPresentBuffers+0xdd862
0a 0813fdfc 08930061     01000000 00000000 00000000 nvoglv32+0xcef6d
0b 0813fe3c 0830d1d2     00000000 00000000 00000140 nvoglv32+0x7f0061
0c 0813fe64 088fe217     00000000 00000000 00000140 nvoglv32+0x1cd1d2
0d 0813fea8 089ea0fb     0a3b1040 00000000 00000d44 nvoglv32+0x7be217
0e 0813fee0 089e9e2f     0a3b1040 08b5b100 08b5b100 nvoglv32+0x8aa0fb
0f 0813fef8 08b5b122     0a867540 07e40080 0813ff18 nvoglv32+0x8a9e2f
10 0813ff08 75d26359     0a867540 75d26340 0813ff74 nvoglv32!DrvValidateVersion+0xaf42
11 0813ff18 778a7c24     0a867540 59857cfe 00000000 kernel32!BaseThreadInitThunk+0x19
12 0813ff74 778a7bf4     ffffffff 778c8ff3 00000000 ntdll!__RtlUserThreadStart+0x2f
13 0813ff84 00000000     08b5b100 0a867540 00000000 ntdll!_RtlUserThreadStart+0x1b

首先来看调用栈为什么不正确

手动推动调用栈,有关调用栈的堆栈信息可以参考我另一篇文章:https://blog.csdn.net/tuan8888888/article/details/113808589

通过打印ebp 的内存,我们知道ebp + 1 的值就是上一层的函数eip。所以推导出来上层函数是08ba784f

和自动推导的调用栈相同,其实调用栈的自动推动也是按照上面堆栈的ebp 找到每一帧eip推导出来的。所以在01栈之前是正确的。

2、所以从01帧开始正向推导调用栈,发现最新的调用栈不正确

因为在函数开始位置081ed2a0 和 当前异常的eip = 08f276bd 相差太远(0xD3 A41D)肯定不在同一个函数中,存在异常跳转。

异常代码分析

我在来看一崩溃的信息,代码中08f276b0 行,调用函数,返回值eax 是否等于0,如果等于等于0 不会触发软中断( int 29h)。所以问题在于08f276b0 函数中,没有正确返回0,发送了中断 29H。

查看资料找到,软中断 int 29h 是由__fastfail 方法产生的,说明是程序主动结束。

fastfail - windows系统的快速失败机制,是一种用于“快速失败”请求的机制,使用最小的消耗来结束进程 ,https://docs.microsoft.com/zh-cn/cpp/intrinsics/fastfail?view=msvc-160

文中特别强调了,User-mode fast fail requests appear as a second chance non-continuable exception with exception code 0xC0000409 ,在用户态fast fail 请求,作为一种“第二次机会非持续异常”,异常码0xC0000409 和本次异常相同。

  11:    __fastfail(7);
005D1093 B9 07 00 00 00       mov         ecx,7
005D1098 CD 29                int         29h 

知道异常退出,但是不清楚具体原因

知道是在函数,接下来我们在分析下esp中有什么内容

0:001> dd esp
0813e884  08b5ac64 00000000 0c870048 6e760038
0813e894  44542041 61682052 65622073 64206e65
0813e8a4  63657465 2e646574 6568540a 70706120
0813e8b4  6163696c 6e6f6974 73756d20 6c632074
0813e8c4  2e65736f 450a0a0a 726f7272 646f6320
0813e8d4  37203a65 7028200a 353d6469 20303237
0813e8e4  3d646974 36393333 63656c20 6165745f
0813e8f4  72656863 6578652e 62323320 0a297469
0:001> dd
0813e904  7369560a 68207469 3a707474 766e2f2f
0813e914  61696469 7375632e 6c656874 6f632e70
0813e924  70612f6d 6e612f70 72657773 65642f73
0813e934  6c696174 695f612f 36332f64 66203333
0813e944  6d20726f 2065726f 6f666e69 74616d72
0813e954  2e6e6f69 00000000 00000000 00000000
0813e964  00000000 00000000 00000000 00000000
0813e974  00000000 00000000 00000000 00000000

对值进行分类

整数:大多数整数将是较小的值,这意味着当显示为DWORD时,它们将大部分为零(例如0x00000270)。

指针:指向本地地址的大多数指针都位于堆栈指针附近(例如fe4cca78)。

状态码:通常以ac(c00000d6)开头。

字符串:可以通过以下事实来识别Unicode和ASCII字 符串:每个字符的范围为20-7f。dc 命令将在右侧显示字符。

柳岸花明,奇迹出现

显示的内容:
A TDR has been detected.The Application must close.同时可以看到内容是error code :7 ,进程id和线程id也是正确的,说明此时确实发生错误。

可以在栈上显示的字符串是字符串数组,例如下面图中展示

https://nvidia.custhelp.com/app/answers/detail/a_id/3633

TDR 的介绍

https://docs.microsoft.com/zh-cn/windows-hardware/drivers/display/timeout-detection-and-recovery

由于通过上面的错误,可以知道是由于接受到TDR的异常导致,所以出现调用栈,中间发送跳转到异常栈位置,所以不正确。

解决方案

https://www.drivereasy.com/knowledge/a-tdr-has-been-detected-nvidia-opengl-driver-error-solved/

1、更新显卡驱动

2、增加TRD的超时时长

windbg - 调试栈溢出及一个崩溃示例相关推荐

  1. [原]windbg调试系列——崩溃在ComFriendlyWaitMtaThreadProc

    前言 这是几年前在项目中遇到的一个崩溃问题,崩溃在了ComFriendlyWaitMtaThreadProc()里.没有源码,耗费了我很大精力,最终通过反汇编并结合原代码才最终搞清楚了事情的来龙去脉. ...

  2. windbg 调试崩溃

    前言 windbg 是非常强大的调试工具,但是在使用windbg 进修调试时候,很多的命令不知道如何使用.文章简单介绍如何使用windbg进行调试 https://docs.microsoft.com ...

  3. windbg + sos 调试w3wp进程内存崩溃问题

    windbg + sos 调试w3wp进程内存崩溃问题 1.加载符合文件 C:\symbols;SRV*C:\symbols*http://msdl.microsoft.com/download/sy ...

  4. WinDBG调试dNet程序总结

    WinDBG工具简介 http://www.cnblogs.com/mashuping/archive/2009/03/28/1424168.html 对于一般的程序不需要使用WinDBG工具去调试, ...

  5. windbg调试堆破坏

    堆破坏 所谓的堆破坏,是说没控制好自己的指针,把不属于你分配的那块内存给写覆盖了.这块内存可能是你程序的数据,也可能是堆的管理结构.那么这个会导致怎样的后果呢?可能的情况我们来yy下 把程序里的计算结 ...

  6. windbg调试HEAP

    HEAP的概念 堆栈堆栈,在操作系统内存中有两种存储空间,一个是堆,一个是栈.堆主要用于存储用户动态分配的变量,而栈呢,则是存储我们程序过程中的临时变量.当然栈的作用远不止用作存储变量,但这不是我们这 ...

  7. windbg调试驱动学习总结

    简单驱动编写与windbg调试 http://trustsec.blog.51cto.com/305338/64694/ 一.驱动编写 随着对windows系统的深入研究,越来越多的内核方面的知识被挖 ...

  8. 博客摘录「 Windbg调试命令汇总」2023年4月15日

    目录 1.Windbg调试器介绍 2.Windbg版本说明 3.Windbg命令汇总 VC++常用功能开发汇总(专栏文章列表,欢迎订阅,持续更新...) https://blog.csdn.net/c ...

  9. Windbg调试命令详解

    Windbg调试命令详解 发表于2013 年 8 月 23 日 转载注明>> [作者:张佩][原文:http://www.yiiyee.cn/Blog] 1. 概述 用户成功安装微软Win ...

最新文章

  1. OpenStack 和 Cloud Foundry
  2. 单细胞数据读取(二)之Read10X读不出来dgCMatrix报错
  3. getBoundingClientRect的用法
  4. BZOJ4855 : [Jsoi2016]轻重路径
  5. 北斗导航 | 多模多频实时GNSS软件接收机
  6. C++TCP和UDP属于传输层协议
  7. springboot初始化逻辑_详解Spring Boot中初始化资源的几种方式
  8. Linux系统管理(10)——Centos8 重启网络服务 网络相关命令
  9. 【Oracle】常用SQL
  10. HTML DOM 基础
  11. CSS3 转换2D transform
  12. Dreammail 下载与安装
  13. 阿里菜鸟java后台开发电话面试
  14. android逆向基础教程一
  15. python编程从入门到实践——16章
  16. 电脑怎么自动锁定计算机,如何设置电脑,人一走开就可以自动上锁?
  17. 赛事快讯|2022中国工程机器人大赛——飞思无人机仿真与自主任务赛项演示视频来啦!
  18. cisco3560及二层交换机配置vlan及常用命令
  19. 读《穷爸爸 富爸爸》
  20. 【华为OD机试真题2023 JAVA】网上商城优惠活动(一)

热门文章

  1. EEG巨型分析I:跨研究的频谱和振幅特征
  2. iphone各个型号屏幕分辨率
  3. 装机员U盘启动PE制作工具V4.0(UEFI+UD)
  4. ubuntu使用python读串口_Win10的Linux子系统Ubuntu使用串口
  5. 软考前该怎样复习才能一次通过?
  6. FPGA 面试经历分享
  7. 央视影音 服务器暂时无法连接服务器,cbox不能播放怎么办 cbox故障解决方法【步骤】...
  8. ADuM磁隔离芯片与6N137光耦隔离比较
  9. linux常用命令合集
  10. Unity红球吃绿球强化学习小任务——Ubuntu20.04系统于2022年2月26日实现