一种栈溢出的场景分析和建议

2021-08-06 15:13:17 浏览数 (4)

场景介绍

有时候当你收到一个dump后,大多数情况可以通过k命令查找到导致栈溢出的函数。但是本文要讲的是,曾经碰到过的栈溢出(stackoverflow), 却无法直接通过k命令查看到当前的函数调用栈。 下面将介绍一个简单的方法,找到导致栈溢出的函数。

样例代码

先声明下,因为产品的实际分析不能够通过网络分享。下面的样例代码,实际上不会导致在上一章场景介绍中提到的问题,可以直接通过crash的栈查找到代码,本文只是通过这个例子来讲解如下场景的分析思路: 如果栈溢出了,通过k命令却无法查找到函数调用栈。

代码语言:javascript复制
#include <iostream>

void Func(const char* pcsPara1, const char* pcsPara2)
{
  char csTmpStr[1000] = "good";
  std::cout << pcsPara1 << pcsPara2 << csTmpStr << std::endl;
  Func(pcsPara1, pcsPara2);
}

int main()
{
  Func("Hello ", "StackOverflow ");
  return 0;
}

样例分析

采用Windbg对dump进行分析。 第一步 通过!teb命令查看当前线程的基本信息, 大家应该都清楚,每一个线程都有自己的函数调用栈。这里着重看一下,这个线程的起始地址为StackBase (000000bfd6b00000), 栈的上限是StackLimit (000000bfd6a01000), 这里同样可以看出来栈是向低地址生长的,而且栈的大小大约为1MB.

代码语言:javascript复制
0:000> !teb
TEB at 000000bfd69b4000
    ExceptionList:        0000000000000000
    StackBase:            000000bfd6b00000
    StackLimit:           000000bfd6a01000
    SubSystemTib:         0000000000000000
    FiberData:            0000000000001e00
    ArbitraryUserPointer: 0000000000000000
    Self:                 000000bfd69b4000
    EnvironmentPointer:   0000000000000000
    ClientId:             0000000000005fd8 . 0000000000000a98
    RpcHandle:            0000000000000000
    Tls Storage:          0000023ec056fdd0
    PEB Address:          000000bfd69b3000
    LastErrorValue:       0
    LastStatusValue:      c000003a
    Count Owned Locks:    0
    HardErrorMode:        0

第二步 通过命令dps查看栈空间上值对应的符号信息。使用这个命令,是因为调用函数的时候会记录函数返回后要执行的下一条指令的地址,那么栈上就保留了函数的信息,通过这个命令就可以查找到对应的函数。(如果对函数调用过程还不熟悉的同学可以参考阅读<<你了解函数调用过程吗?>>)

代码语言:javascript复制
0:000> dps 000000bfd6a01000 000000bfd6b00000
......
000000bf`d6afd148  00007ff6`5a461755 StackOverflow!Func 0xa5
......
000000bf`d6afd588  00007ff6`5a461755 StackOverflow!Func 0xa5
......
000000bf`d6afd9c8  00007ff6`5a461755 StackOverflow!Func 0xa5
......

第三步,可以通过栈上的值对应的符号信息,查找到可能的函数调用过程,比如这个例子看到多处调用StackOverflow!Func, 则可以对相应的代码处进行审查,得知这是一个栈溢出问题。

建议

其实对于栈溢出来说,很多情况都是函数非正常的循环调用导致, 此时修复这个bug即可。但也有可能是函数的局部变量空间过大,函数调用层数深。比如在一些领域禁止使用在堆上分配/使用内存,很多时候只能在栈上去申请,这个时候提高栈空间的默认值(1M),如下图将栈空间调整为4M。

1 人点赞