一个strcpy引发的血案

时间:2022-09-20 07:26:15

一个strcpy引发的血案

就在离下班还有半个小时的时候,M君突然跑来说,“今天给客户的软件,客户刚装上一运行就崩溃,这是给客户看效果,还没有卖出去…….”。我心里就想骂,我刚接手这个项目才一周,代码还没有熟悉,上层也就我一个,重要的是,这个软件是8.0的,我接手的是9.1的,我连8.3的代码都没有。上SVN看看,最后一个版本是2015年9月提交的,这个时间,我还没来公司,8.0的我连界面都不知道长什么样。要是平时解决这个停止响应的还行,关键是在给客户演示,客户装上都运行不起来,情况是多紧急。。。哎,抱怨又没啥啥用,还是快解决吧。

​ 向销售人员要了那个客户的QQ,然后问他是什么操作导致的停止响应,他说刚装上,一运行就崩溃,而且是必现的,软件根本没法使用。我又问了一些电脑的信息,系统是什么,32位还是64位等等,但是几乎没有什么用。在问的过程中,我也把相应版本下载了,在虚拟机里面安装了,但是一切正常。这种和平台相关的,偶尔某个机器出问题的bug,应该最让人恼火了。。然后我问他电脑上有没有装teamviewr,他说有,然后就远程看。(其实我心里一直在抱怨,之前是怎么测试的呀,这么久了,都没有测出这个问题,,我能不能解决出来呀等等)

​ 首先我抓取了程序崩溃的dump,一般情况下,从dump还是能看出来一些东西的。

  • 用管理员权限打开命令提示符界面,输入taskmgr。
    一个strcpy引发的血案

  • 找到崩溃的程序,右键,创建转储文件,这个东西保存在临时目录。是一个.dump后缀的文件。
    一个strcpy引发的血案

  • 然后用windbg分析了下,但是没有找有有用的东西,也不能找到出错的模块。(这个项目是10几年的老项目,几乎没有注释,30几个模块组成一个大的,简直把人看的想砸电脑,偶尔看到一两条无关紧要的注释,还是5 6年前的时间 某某某 怎么怎么的。如果定位不到某一个模块,就不好找问题了。)

  • 我重新编译了一下主程序,然后又在客户那里远程试,还是崩溃,然后又把dump抓出来,这次在windbg的符号表路径加入我编译后的pdb,之前因为太老了,没有pdb,看不出来崩溃在哪里,心想这次应该可以看出来一点有用的东西了吧,但是一无所获。。。

  • 然后我从客户那里调出来程序的运行日志,然后一看,只打印了两条日志,还是乱码。。。。
    一个strcpy引发的血案
    真让人崩溃,但是里面还是一个有用的信息,”Readip”,太开心了。然后找了一下这个日志打印的地方,瞬间心情又不好了,丫的是一个公共文件,也就是说很多很多地方都包含的这个文件,调用地方也是多了去了。。。跟下程序过程吧,然后。。。突然。。此时已经过去半个小时了,客户发来消息说他要去接孩子,问我能解决不,我犹豫了一分钟说,能,6点给你回复。

  • 本来还想安心的调试,结果心情一下紧张起来了,没时间了。。然后从程序开始,初始化的地方都加上日志,看看在哪里出错了,然后再进入相应的模块去继续跟踪。不得不说,这个宏简直还好了,你可以直接到处复制这条打印语句,都可以定位到文件的地方。
    一个strcpy引发的血案

  • 然后瞬间程序里面多了几十条打印日志的语句,然后在客户那里试了后,拿出日志看,找到从哪里断开了,没有打印。终于找到了一个可能点,获取网络设备的时候,打印出来那些乱码,并且在那里断开了。因为这些数据是从底层获取的(这个程序分成底层和上层,底层是对硬盘的一些操作,将一些数据在硬盘的某个位置放着),开始怀疑是底层数据出错了,但是检查了下,某些底层的数据还是能正常获取的,即使数据有误,停止响应还是上层的问题。
  • 然后又开始我的二逼做法,继续从怀疑点打日志,然后缩小范围,跟踪到响应的函数然后再继续跟踪。皇天不负程序员呀,终于定位到了一个函数,然后定位到了某一行。
    一个strcpy引发的血案
  • 但是这行明显没有什么错误呀,参数正常,但是程序执行到这里就崩溃了。然后我把AdapterInfo和dwBuflen打印出来看看,发现dwBuflen的值很大,不正常。但是这个值是个局部变量,刚在上面几行定义的,中间就赋值了一次,之后没有任何操作,总共就出现了三次。
    一个strcpy引发的血案
  • 然后我在客户机器上远程测试,发现赋值后面打印了出dwBufLen的值,是正确的,在使用的前打印就出问题。出现这种情况很有可能是其他语句执行后溢出了,覆盖了dwBufLen的值。然后我在执行GetAdaptersInfo的前面,重新给dwBufLen赋值,这时程序正常了。
  • 当然将dwBufLen在后面赋值程序时可以了,也可以给客户交代了,但是程序中存在溢出情况,这里正常了,其他地方肯定就又出问题了。看了下代码,发现一个可疑的函数。strcpy(chT,AnimateWindow1(ch));,要溢出的话,这个函数就有可能了,其他的是在也发现不了问题。然后在客户机器上试了下,果然。。。
    一个strcpy引发的血案
  • 在另一个模块里面找到CKIP里面看了下,发现返回值可谓是千奇百怪呀,各种,还有汉语提示。chT定义的长度只有30。明显以前这两个模块的人就没有沟通下,定义的长度都差的远。。。。而且你最后只用到返回值的前两个字符,有没有必要把整个字符拷贝到chT中呀。
    一个strcpy引发的血案
  • 然后用memcpy代替strcpy,问题成功解决。终于在客户可接受的时间内,给客户解决了问题。然后重新给客户打包了一个程序,发了过去。

这个问题的收获就是

1. 一定要和不同模块的沟通好,并且必须也要了解对方模块的功能,这样才能更好的对接。

2.不要用strcpy,不要用strcpy,不要用strcpy,可以用不要用strcpy_s,或者memcpy。

3.FILE,LINE这两个宏简直太好了。