1 现象:问题描述
产品A在测试中偶然发现XXX模块在处理消息跟踪命令下发时core,提供core文件和操作日志。产品A运行环境是solaris平台。
2 关键过程:根本原因分析
dbx 分析;core在如下函数(消息跟踪命令组装处理)。而最可怕的是该函数栈已经乱掉。不能具体定位到哪一条语句core,不能察看相应变量的值。
void TCmdGenerator::genCommand(const char* cmdHeader, const STParamList& paramsList, char* &cmdOut)
{
FUNC_TRACE(I2K_MED_TAG, "TCmdGenerator::genCommand()");
char tmpCmd[1028] = {0};
char tmp[256] = {0};
ACE_OS::strcpy( tmpCmd, cmdHeader );
ACE_OS::strcat( tmpCmd, ":" );
size_t paramCount = paramsList.size() - 1;
//TODO:如果参数个数小于1, 则调用者输入不正确;
for( size_t i = 0; i < paramCount; ++i )
{
const STParam& param = paramsList[i];
ACE_OS::strcpy( tmp, param.Name.c_str() );
ACE_OS::strcat( tmp, "=" );
ACE_OS::strcat( tmp, param.Value.c_str() );
ACE_OS::strcat( tmp, "," );
ACE_OS::strcat( tmpCmd, tmp );
}
if( paramCount > 0 )
{
const STParam& param = paramsList[paramCount];
ACE_OS::strcpy( tmp, param.Name.c_str() );
ACE_OS::strcat( tmp, "=" );
ACE_OS::strcat( tmp, param.Value.c_str() );
ACE_OS::strcat( tmp, ";" );
ACE_OS::strcat( tmpCmd, tmp );
}
delete []cmdOut;
cmdOut = new char[ACE_OS::strlen(tmpCmd) + 1];
ACE_OS::strcpy( cmdOut, tmpCmd );
return;
}
是什么原因导致该函数栈乱掉?检视上述函数代码,发现有两个地方可疑:
1)char tmpCmd[1028];如果命令的条件个数很多,命令最终可能超越1048,执行如下语句时tmpCmd越界,导致栈乱掉
ACE_OS::strcat( tmpCmd, tmp );
2)char tmp[256];如果某个条件名,或者条件值很大,超过256个字符,在执行如下4条语句时tmp越界,导致栈乱掉
ACE_OS::strcpy( tmp, param.Name.c_str() );
ACE_OS::strcat( tmp, "=" );
ACE_OS::strcat( tmp, param.Value.c_str() );
ACE_OS::strcat( tmp, "," );
查看对应时间点的日志,发现条件个数达20多个,计算这些条件和条件值,其长度约2000,超过 1028;tmpCmd越界,导致栈乱掉
3 结论:解决方案及效果
解决方案:
用string tmpCmd = "" 替代如下两个局部字符数组变量
char tmpCmd[1028] = {0};
char tmp[256] = {0};
从而避免可能出现的字符数组越界,导致栈乱掉
代码如下:
void TCmdGenerator::genCommand(const char* cmdHeader, const STParamList& paramsList, char* &cmdOut)
{
FUNC_TRACE(I2K_MED_TAG, "TCmdGenerator::genCommand()");
string tmpCmd="";
tmpCmd.append(cmdHeader);
tmpCmd.append(":");
size_t paramCount = paramsList.size() - 1;
//TODO:如果参数个数小于1, 则调用者输入不正确;
for( size_t i = 0; i < paramCount; ++i )
{
const STParam& param = paramsList[i];
tmpCmd.append(param.Name.c_str());
tmpCmd.append("=");
tmpCmd.append(param.Value.c_str());
tmpCmd.append(",");
}
if( paramCount > 0 )
{
const STParam& param = paramsList[paramCount];
tmpCmd.append(param.Name.c_str());
tmpCmd.append("=");
tmpCmd.append(param.Value.c_str());
tmpCmd.append(";");
}
delete []cmdOut;
cmdOut = new char[tmpCmd.length() + 1];
ACE_OS::strcpy( cmdOut, tmpCmd.c_str() );
return;
}
效果:
按照上面方案修改后,执行同样的操作,core问题没有重现。问题得到解决。
4 经验总结:预防措施和规范建议
这是一个字符数组越界引起函数栈乱掉,从而导致core的典型问题。
函数处理中要关注可能的字符数组越界。对这些字符数组可以采取:
1) 越界保护:对长度进行检测,如果超过数组长度,做好相应得保护处理。
2) 用长度保护的函数如strncpy,strncat 替代 strcpy,strcat
3) 用string,vector等替代字符数组
5 备注
本问题出现在转测试阶段。
6 考核点
字符数组长度越界的保护措施。
7 试题
函数处理中,由于传入参数的原因,可能导致函数中定义的字符数组越界,我们可以采取哪些保护措施()
A:越界保护:对长度进行检测,如果超过数组长度,做好相应得保护处理
B:用长度保护的函数如strncpy,strncat 替代 strcpy,strcat
C: 用string,vector等替代字符数组
D:越界是由于传入参数的问题,由函数调用者保护,函数中不作任何处理
答案:A,B,C
阅读(752) | 评论(0) | 转发(0) |