Klockwork告警常见错误

时间:2022-07-21 07:12:16

下面列举的是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))
 
数组越界也是经常犯的错误,对数组的访问,加一下检查;考虑到会频繁出现数组下标的检查,可以写一个类似上面的宏;