2012年7月7日星期六

Buffer overrun detected!

Buffer overrun detected!
     -- 一个C++栈缓冲区溢出警告(弹窗)

> 问题现象
先看看,程序运行过程中弹窗报错,如下图所示:


===
有人见过这种情况吗?最近有些(迅雷)用户遇到这种情况,很诡异。奇怪的地方在与我们开发与测试这边的环境下都不曾出现过这样的错误情况(否则早已解决了☺)。只有在极个别的用户机器上才出现这样的弹窗报错情况,但程序又不崩溃只是弹窗警告,点击确定程序即继续正常工作了,诡异的是这样弹窗报错竟然还有一定的周期性,大概10分钟一次。无语了。


> 网上说法
 从网上搜索得到下面几种说法,包括:
 a,安全程序(软件)包括防火墙,杀毒软件,安全卫生,管家等,可能引起程序的不兼容导致这样的情况。
 b,系统升级打补丁,导致之前正常运行的程序出现这样的错误,只要针对XP系统sp3。
 c,驱动程序由于升级版本与之前正常运行的程序出现不兼容,导致程序报错。
 ……


 具体的网上参考链接,有:
http://topic.csdn.net/u/20080617/17/0d16e504-0af9-40d5-911e-eee9fcdfdb75.html
http://topic.csdn.net/u/20080617/10/f3b02b0a-fa29-49c4-b4fc-c21ccc
http://answers.microsoft.com/en-us/windows/forum/windows_other-gaming/microsoft-visual-c-runtime-library-buffer-overrun/bb1cb707-622d-4fc6-b46d-c2bb4dec6207

其中,微软的官网论坛有这么句描述问题的,如下:
"The problem is due to a known bug in the ATI drivers - there are several threads on it on the AMD site. The thread I read was closed by a moderator with the statement "It will be fixed soon". I'm running the 10.11 drivers on a 4870 and I have the problem too."

===

> 问题分析
经过与出现该问题的用户的多次沟通,以上(网上得来的)说法均在用户环境一一确认,我们的情况不然,所以,这些说法都可以排除掉。
后来,好不容易从一个用户那重新问题,并顺利抓取了出错程序的堆栈信息(dmp文件)。这里还有一个小小的意外故事,当时让用户将dmp文件传过来,过程中刚好公司意外断点了,结果泡影了,所以赶紧手机登录上线让用户发到qqmail……还好,这位用户比较热心,帮dmp文件发到了邮箱。

再后来,分析了dmp文件,显然有了堆栈信息问题明确好多了。弹窗报错的原因在这里,详见下面说明:
(引用自\Microsoft Visual Studio .NET 2003\Vc7\crt\src\secchk.c)
/***
*seccook.c - checks buffer overrun security cookie
*
*       Copyright (c) Microsoft Corporation.  All rights reserved.
*
*Purpose:
*       Defines compiler helper __security_check_cookie, used by the /GS
*       compile switch to detect local buffer variable overrun bugs/attacks.
*
*       When compiling /GS, the compiler injects code to detect when a local
*       array variable has been overwritten, potentially overwriting the
*       return address (on machines like x86 where the return address is on
*       the stack).  A local variable is allocated directly before the return
*       address and initialized on entering the function.  When exiting the
*       function, the compiler inserts code to verify that the local variable
*       has not been modified.  If it has, then an error reporting routine
*       is called.
*
*       NOTE: The ATLMINCRT library includes a version of this file.  If any
*       changes are made here, they should be duplicated in the ATL version.
*
*******************************************************************************/
……

但是,具体的错误根源,虽然找到了错误点(疑点),但还需程序验证确认问题解决了,不出现弹窗报错方知真相。
程序错误点其实很简单,只是隐藏在大几十万行代码里头,不好找出来,而且奇怪的是那里的代码一直也没啥改动的啊?
===


> 解决方案
该程序逻辑涉及一个Windows API 读取注册表一个键值,接口如下:
LONG WINAPI RegQueryValueEx(
  __in          HKEY hKey,
  __in          LPCTSTR lpValueName,
  LPDWORD lpReserved,
  __out         LPDWORD lpType,
  __out         LPBYTE lpData,
  __in_out      LPDWORD lpcbData
);

(该函数的具体说明参考MSDN
注意,最后一个参数lpcbData为__in_out类型,既是输入又是输出参数。

其中错误程序,描述如下:
void function() 
{
     ...
     char buffer[MAX_PATH]
     unsigned size; // Error:这里没有初始化。
     if (ERROR_SUCCESS == RegQueryValueEx(..., buffer, size)) {
          ...
     }
     // Error:这里也再没有初始化size,此时size为上次RegQueryValueEx操作的读取字节数。
     if (ERROR_SUCCESS == RegQueryValueEx(..., buffer, size)) {
          ...
     }
     ...
} // BTW,从堆栈信息看来,运行出错的程序是在这里函数退出时,弹窗报错的!

修正程序,如下:
     char buffer[MAX_PATH]
     unsigned size = MAX_PATH;
     if (ERROR_SUCCESS == RegQueryValueEx(..., buffer, size)) {
          ...
     }
     size = MAX_PATH;
     if (ERROR_SUCCESS == RegQueryValueEx(..., buffer, size)) {
          ...
     }





结果程序运行正常了,不再报错了!


===
最后,仍然心存疑问的是,“为啥这样的程序出错不是必须的,而且是很稀罕的?”。
猜想:因为变量size未初始化,其值为未确定状态。导致程序出错的概率,很可能跟系统(C++运行时)内存栈环节有关。


(完)

没有评论:

发表评论