现象、问题描述
安全后台函数 void ShowErrorMessage( const RWDBStatus& status ) 用于显示参数status中的错误详细信息。某次数据库执行错误后,进程coredump。经检查发现,是在调用该函数后出现异常。
关键过程、根本原因分析
函数 void ShowErrorMessage( const RWDBStatus& status ) 中的代码很简单,如下:
……
char temp[300];
ACE_OS::sprintf(temp, "DBTools error code:\t%d", status.errorCode());
iMapOutputString(temp);
ACE_OS::sprintf(temp, "DB message:\t%s", status.message().data());
iMapOutputString(temp);
……
ACE_OS::sprintf(temp, "%s", status.vendorMessage2().data());
iMapOutputString(temp);
调试发现,在执行 ACE_OS::sprintf的时候,status.vendorMessage2().data() 返回的是const char*,其长度为534字节,超出了temp的边界,造成字符串拷贝内存溢出,在该函数退出时,函数栈析构局部变量temp时coredump。
结论、解决方案及效果
1、 设置temp长度为1025,在调用ACE_OS::sprintf前判断参数的长度,如果大于1024,就不进行拷贝,而直接提示别的信息。
2、 也可以直接把 status.vendorMessage2().data() 作为参数传入iMapOutputString中。
经验总结、预防措施和规范建议
这个问题实际上是一个很简单的内存溢出问题,但是在我们的代码中很常见:定义一个字符数组,然后直接对该数组进行拷贝操作。我们知道,C的字符指针操作是不做边界检查的,一旦边界溢出,后果必然是灾难性的。
这个函数在代码(包括移植代码)中已经使用了几年了,一直运行的没有问题,是因为遇到的错误码长度一般都是100多字节。但是,只要是不合理的,纵然发生的几率只有千分之一,终究还是会发生的,如果发生在网上,就会引起很严重的后果。
对字符数组、指针操作一定要考虑边界溢出的问题,建议尽量减少使用C的底层字符指针操作,使用代码安全的std类。
备注
无。
考核点
字符数组越界
试题
如下说法,哪些是不正确的(C)
A: 尽量用vector和string取代数组
B: 给char数组赋值前一定要先检测赋值数据的长度,如果长度超过数组长度,进行必要处理,避免数组越界,造成缓冲区溢出
C: 根据估算,赋值数据长度不会超过char数组长度,为了代码简洁,因此对char数组赋值时可以不用考虑数组越界,不用进行长度检测处理
D:定义数组时需要考虑栈溢出
阅读(482) | 评论(0) | 转发(0) |