klocwork扫描后的常见问题是分等级的:
1:Critical 2:Error 3:Warning 4:Review 等。
目前的部门要求处理1-2两个等级的问题,常见问题有以下几类:
1、函数中的异常处理缺少返回值
类似这种表述:
28 (Local) /home/cps/VersionUpdateTool/DBConfig_navigate.cpp:3626 FUNCRET.GEN (1:Critical) Analyze
Non-void function does not return value
Current status 'Analyze'
----------------------------------------
这种问题的处理,不用说就很简单啦,下面是网上的一个例子:
WORD32 Test(WORD32 *p, WORD32 *q)
{
WORD32 dwBindType = 0;
if ((NULL == p) || (NULL == q))
{
SF_ASSERT(0);
return SFWD_ERROR; //导致与预期返回的结果不符
}
return SFWD_OK;
}
断言需要做异常处理的,并不是断言就完事了,如果tr掉,继续往下走,可能会出现跑飞或单板重启等严重问题;
在release版本,断言是被忽略的;debug版本,适当增加断言,当出现异常时,容易定位问题。
2、函数在异常退出前务必要释放先前申请的内存
类似下面这种表述:
27 (Local) /home/cps/VersionUpdateTool/DBConfig_navigate.cpp:349 MLK.MIGHT (2:Error) Analyze
Possible memory leak. Dynamic memory stored in 'object' allocated through function 'new' at line 276 can be lost at line 349
* DBConfig_navigate.cpp:271: Continue loop iteration, while j<s->_obIds.size() is true
* DBConfig_navigate.cpp:276: Dynamic memory stored in 'object' is allocated by calling function 'new'.
* DBConfig_navigate.cpp:349: Dynamic memory stored in 'object' is lost.
Current status 'Analyze'
----------------------------------------------
这种问题的处理大家也是一目了然,就是把一些new过的对象,忘记在某些位置释放的,按照报错的提示进行释放即可。
这种问题经常出现在某个函数内,中间有return或者continue的地方。
下面还是网上的一个例子:
SOCK_PKTINFO* Test(WORD32 dwReplyType)
{
BYTE *pValidatePkt = NULL;
SOCK_PKTINFO *pPktInfo = NULL;
pValidatePkt = (BYTE *) XOS_GetUB(VER_REPLY_PKTLEN);
if (NULL == pValidatePkt)
{
ROSNG_TRACE_ERROR("Error: XOS_GetUB failed!\n");
return NULL;
}
MEMSET(pValidatePkt, 0, VER_REPLY_PKTLEN);
if (TOPO_REPLY_OK == dwReplyType)
{
MEMCPY(&pValidatePkt[4], "YES", 4);
}
else
{
SF_ASSERT(0);
XOS_RetUB(pValidatePkt);
return NULL;
}
return pPktInfo;
}
这段代码其实有几个问题:
1.没有遵守谁申请谁释放的原则,C语言很容易出现内存泄漏,但遵守规范,这种泄漏就会少很多;
除了申请的内存需要缓存而不是在一个消息中释放外(比如分片组包的情况),报文处理中基本上可以做到谁申请谁释放;
2.在异常流程,没有释放内存,这个是非常容易犯的错误;
3.如果是临时申请内存,应该使用PUB_GetLocalUB/PUB_RetLoacalUB这对接口;
再来一个例子:
case *******
{
*****************
*******************
if(nums!=0)
{
CPS_ORM_msFlush(m_wiringSmHandle, 1);
//delete[ ] obIds;
}
if(obIds!=NULL)
{
delete[ ] obIds;
}
break;
}
case MULTIPLE_CHOICE_DATA:
{
。。。。。。。。。
上面的例子中,如果重复delete,也会报错哦!
3、函数内对空指针进行引用操作
类似下面这种表述:
32 (Local) /home/cps/VersionUpdateTool/md5.cpp:267 NPD.FUNC.MUST (1:Critical) Analyze
Pointer 's' returned from call to function 'strdup' at line 263 may be NULL and will be dereferenced at line 267.
* md5.cpp:263: 'mstr' is assigned the return value from function 'strdup'.
* md5.cpp:265: s = mstr
* md5.cpp:267: 's' is explicitly dereferenced.
Current status 'Analyze'
-------------------------------
这类问题的处理办法也没啥说的,就是使用前记得判断是否为NULL:
主要注意的是,判断后如果需要return,记得看一看问题2,是否需要释放内存,否则又会报出内存泄露的问题。
下面还是网上的例子:
VOID Test(SFWD_NBR_INFO *pNbrInfo)
{
WORD32 *pIndex = NULL;
if(NULL == pNbrInfo)
{
ROSNG_TRACE_ERROR("Error: Para is NULL!\n");
SF_ASSERT(0);
return;
}
if(0 == pNbrInfo->wActiveNbrNum)
{
pIndex = sfwd_node_new(sizeof(WORD32));
if(NULL == pIndex)
{
ROSNG_TRACE_ERROR("Error: sfwd_node_new return NULL!\n");
return;
}
* pIndex = pVifInfo->dwIndex;
}
XOS_RawPrint("gIndex = %d, pNbrInfo->dwNbrStatus = %d\n", * pIndex, pNbrInfo->dwNbrStatus);
//这里如果没有执行上面if语句内的代码,就会导致对空指针进行操作;
return;
}
不管是打印还是语句,只有是访问指针指向的内容,先想想这个指针有没有为空或非法地址的情况;
4、函数中数组操作的越界访问
类似这种表述:
(Local) /home/cps/VersionUpdateTool/xml_split.h:111 ABV.TAINTED (1:Critical) Analyze
Array 'path' of size 1024 may use index value(s) 1024
* xml_split.h:107: Array 'path' size is 1024.
* xml_split.h:111: 'path' is passed as an argument to function 'strncat'.
* OS_NS_string.inl:262: 'strncat' is called.
* xml_split.h:108: Array 'path' of size 0 is retrieved from function 'memset'.
* xml_split.h:111: Result of expression 'charlength($1)+min(charlength($2),$3)' is [0,1024].
Current status 'Analyze'
-------------------------------------------------
这类问题的处理在初始化时约定数组长度,并且在需要时判断长度即可。
下面是一个网上的例子:
WORD32 Test(SFWD_TOPO_PKT_CAP_IF_INFO_T *pIfInfo, WORD32 gIndex)
{
WORD32 dwSubslot = 0;
WORD32 dwSubindex = 0;
WORD16 if_type = 0;
if (NULL == pIfInfo)
{
ROSNG_TRACE_ERROR("sfwd_topo_pkt_cap_get_ifinfo: inParam is NULL!\n");
SF_ASSERT(0);
return FALSE;
}
if ((gIndex <= 0))
{
ROSNG_TRACE_ERROR("sfwd_topo_pkt_cap_get_ifinfo: Para error!\n");
SF_ASSERT(0);
return FALSE;
}
dwSubslot = gIndex / MAX_IF_NUMBER_PERCARD;
dwSubindex = gIndex % MAX_IF_NUMBER_PERCARD;
if((dwSubslot >= MAX_SUBCARD_NUMBER) || (dwSubindex > MAX_IF_NUMBER_PERCARD))
{
ROSNG_TRACE_WARNING("sfwd_topo_pkt_cap_get_ifinfo: invalid subslot or subindex.\n");
SF_ASSERT(0);
return FALSE;
}
if_type = gInterface[dwSubslot][dwSubindex].wType;
//在对gInterface数组进行访问时,如果dwSubslot、dwSubindex的下标值超出了该数组声明的大小可能产生越界。所以,需要根据代码的上下文来对数组的下标做有效性检查(并不是所有的数组访问都检查,有些是没必要的)
return TRUE;
}
#define SFWD_SUBCARD_NO_CHECK(A, B) ((A >= MAX_SUBCARD_NUMBER) || (B > MAX_IF_NUMBER_PERCARD))
数组越界也是经常犯的错误,对数组的访问,加一下检查;考虑到会频繁出现数组下标的检查,可以写一个类似上面的宏;
5、某些对象未初始化
类似这种表述的为未初始化:
(1)
(Local) /home/cps/VersionUpdateTool/main.cpp:705 UNINIT.STACK.MUST (1:Critical) Analyze
'wf.ulKeyNumber' is used uninitialized in this function.
* main.cpp:700: 'wf.ulKeyNumber' is declared.
* main.cpp:705: 'wf.ulKeyNumber' is used, but is uninitialized.
Current status 'Analyze'
(2)
20 (Local) /home/cps/VersionUpdateTool/VersionUpdate_workbench.cpp:3682 UNINIT.STACK.MIGHT (1:Critical) Analyze
'modelVersionShowDlg.m_pQuitBtn' might be used uninitialized in this function.
* VersionUpdate_workbench.cpp:3615: g_versionName==2||g_versionName==3||g_versionName==5 is true
* VersionUpdate_workbench.cpp:3681: 'modelVersionShowDlg.m_pQuitBtn' is declared.
* VersionUpdate_workbench.cpp:3682: 'modelVersionShowDlg.m_pQuitBtn' is used, but is uninitialized.
Current status 'Analyze'
这类问题也很好处理,在构造或定义时,初始化即可。