下面列举的是Klockwork告警中常见的告警形式,这些情况在编译阶段都不会报出来语法上的错误,并且在运行阶段执行到的概率很小。但是在某些场景下一旦执行到了这些语句, 很可能引起进程的跑飞和挂起。
1、没有对所有入参指针做有效性检查
WORD32 Test(WORD32 *p, WORD32 *q)
{
WORD32 dwBindType = 0;
if ((NULL == p) || (NULL == q))
{
SF_ASSERT(0);
return SFWD_ERROR;
}
return SFWD_OK;
}
编程规范要求,所有入参是指针,都必须进行判空操作;如果是入参是整型等,可以根据需要决定是否对入参进行检查;但参数不合法都要做异常处理;
2、函数中的异常处理缺少返回值
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版本,适当增加断言,当出现异常时,容易定位问题。
3、函数在异常退出前务必要释放先前申请的内存
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这对接口;
4、函数内对空指针进行引用操作
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;
}
不管是打印还是语句,只有是访问指针指向的内容,先想想这个指针有没有为空或非法地址的情况;
5、函数中数组操作的越界访问
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))
数组越界也是经常犯的错误,对数组的访问,加一下检查;考虑到会频繁出现数组下标的检查,可以写一个类似上面的宏;