1、返回值处理
① 对返回空的指针没有检查有效性就使用,导致隐患;
EAP_HANDLER_S *EAP_Handler()
{
...
pstHandler = EAP_Handler_New(pstInstance);
if(NULL == pstHandler)
{
return NULL;
}
...
}
ULONG EAP_OFFLOAD_Authentication()
{
pstTunnelHandler = EAP_Handler(pstInstance);
if(ERROR_SUCCESS != EAP_MSCHAPV_TryOffLoad(pstTunnelHandler))
{
....
}
}
ULONG EAP_MSCHAPV_TryOffLoad(pstHandler))
{
pstEapDs = pstHandler->pstEAP_Ds; // 可能访问空指针
......
}
② 对执行失败的情况没有处理,导致隐患;
ULONG SVPN_UAM_AddResGroup()
{
pstResGroup = SVPN_Pub_SafeMalloc( ...,sizeof(SVPN_UAM_RESGROUP_S));
if(NULL == pstResGroup)
{
return ERROR_FAILED;
}
(VOID)SVPN_UAM_AddResGroupToArray(pstResGroupHead, pstResGroup);
...
}
ULONG SVPN_UAM_AddResGroupToArray(SVPN_RESGROUP_HEAD_S *pstResGroupHead,
SVPN_UAM_RESGROUP_S *pstResGroup)
{
ULONG ulIndex = 0;
ulIndex = SVPN_UAM_RESGROUP_INDEX(...);
if(NULL != pstResGroupHead->ppstResGroup[ulIndex])
{
return ERROR_FAILED;
}
}
③ 将不是BOOL类型的值强制转化为布尔类型;
ULONG NAT_DisplayStatistics(...)
{
ulRet = IPC_RPC_SynCall();
if( (BOOL_T)ulRet ) // 应该更换为 if(ERROR_SUCCESS != ulRet)
{
return ERROR_FAILED;
}
}
④ 返回值比较的目标不是操作成功的返回值;
#if ( CONFIG_HA == TRUE )
if(ERROR_SUCCESS != HA_GetBoardType) // 此处应该修改为if(HA_AMB != HA_GetBoardType)
{
return ERROR_SUCCESS;
}
#endif
2、断言的使用
2.1、对程序运行中可能发生的情况使用断言处理
断言用于程序运行中不应该发生的情况进行检查;条件判断用于对程序运行过程中可能发生的情况进行检查
① 断言中不能含有业务逻辑,只能是逻辑表达式;
DBGASSERT (ulLen = (ULONG)strlen(pcString));
DBGASSERT(ERROR_SUCCESS == IF_GetDataByIfType(...,IF_TEMPLETNAME,,(szIPCLITemplate)));
② 对能发生的情况断言;
pszTemp = MEM_Malloc(MID_8021X|SID_DOTIX_EAD,usDataLen);
if( pszTemp == NULL )
{
DBGASSERT(0);
return ERROR_FAILED;
}
断言推荐用法:
① 模块当前支持的规格假设
ulNameLen = (ULONG)strlen(pHostName);
DBGASSERT(ulNameLen < DNS_DOMAINNAME_LEN);
② 模块的实现约束或者流程假设
switch(ulPrintType)
{
case ACFP_PRINT_SERVER:
{
break;
}
default:
{
DBGASSERT(0);
break;
}
}
③ 函数参数合法性检查
ULONG DOTIX_TxReqID(IN_SC_MSG_S *pstMsg)
{
DBGASSERT(NULL == pstMsg)
......
}
3、系统资源的使用
① 资源的申请释放不在同层次;
VOID ETH_VEMacOnMain(IN ULONG ulIfIndex ......)
{
(VOID)IF_SelectHandle_Register(MID_ETHER, &ulSelHandle);
while(BOOL_TRUE == IF_IsValidIfIndex(ulWalkIfIndex))
{
...
if(ulIfSlot == ulVERelySlot)
{
...
IF_SelectHandle_UnRegister(&ulSelHandle);
return;
}
}
return;
}
② 在成对的系统资源操作之间异常退出
ULONG SVPN_SCFM_SaveDomainCfgFile()
{
ulFileID = SVPN_SCFM_FOpen();
...
if(ERROR_FAILED == ulRet)
{
...
return ERROR_FAILED;
}
SVPN_SCFM_FClose();
return ulRet;
}
③ 资源申请应该在合法性检查和前提条件OK之后进行
VOID VRRP_SendLogPacketError()
{
pduMsg = IC_CreateMsgPdu();
if(NULL == pduMsg)
{
return;
}
if(pstStandBy->ucPacketErrorType == (UCHAR)ulType)
{
pstStandBy->ulCorrectPacketCnt = 0;
return;
}
}
④ 将申请的资源直接赋给间接变量:多级指针和全局变量
#define MAC_ADDRESS_LEN
#define MAC_LEN
*ppSendData = MEM_Malloc(MID_XXX,sizeof(UCHAR)*MAC_ADDRESS_LEN);
if(NULL == *ppSendData)
{
return ERROR_FAILED;
}
MEM_Copy(*ppSendData,aucMacAddr,MAC_LEN);
ULONG MWI_AddEtity(...)
{
pstEntity->pstAuthInfo = MEM_Malloc(MID_VOICE_SIP, sizeof(SIPAUTHINFO_S));
if(NULL == pstEntity->pstAuthInfo)
{
return ERROR_FAILEDL;
}
MEM_Copy(pstEntity->pstAuthInfo, pstAuthInfo, sizeof(SIPAUTHINFO_S));
...
if(NULL == pstLastEntity)
{
return ERROR_FAILED;
}
}
4、内存释放
① 错误的释放函数
ULONG QOS_ATMSecondHalfTrans(IN ULONG ulIfIndex, IN MBUF_S *pstMBuf, IN VOID *pQoSDB)
{
...
pstAtmPVC = (ATM_PVC_S *)(MBUF_GET_LINK_ATM_PVCINFO(pstMBuf));
if(NULL != pstAtmPVC)
{
return QOS_ATMPVC_Transmit(pstAtmPVC, pstMBuf);
}
MEM_Free(pstMBuf); /* 此处应该调用MBUF_Destroy函数*/
return ERROR_FAILED;
}
② 重复释放;
ULONG RD_PKT_AccessReject_Proc(RD_fmtPkt_s *pstPkt)
{
pstAuthReject = MEM_Malloc(...,sizeof(SC_AUTHENFAILURE_S));
if(NULL == pstAuthReject)
{
return SC_FAILED;
}
....
ulRet = RD_SendMsg(SC_MODULE_SRVCTRL,usMsgType,(ULONG)usUserIndex,
SC_OE_RDS_REJECT,(ULONG)pstAuthReject);
if(SC_FAILED == ulRet)
{
MEM_Free(pstAuthReject);
}
}
③ 释放后再使用
ULONG FR_InarpReportSend(FR_INTERFACE_S *pstFrIf,....)
{
(VOID)FR_SecondHalfTransmit(ulIfIndex, pMBuf, NULL);
...
FR_DEBUG_PACKET(pstFrIf, FR_PAC_OUT, &stFhQ992, ulMsgLen, (ULONG)FR_PAC_ARP, pMBuf); // FR_SecondHalfTransmit函数中pMBuf已经被释放
}
④ 释放前没有摘链
VOID PT_WEB_DelUser(IN_DLL_NODE_S *pstNode)
{
pstUserInfo = (PT_WEB_USER_S *) DLL_GET_HANDLE(pstNode);
MEM_Free(pstUserInfo);
pstUserInfo = NULL;
DLL_Delete(&g_stPtWebUserDll, pstNode);
MEM_Free(pstNode);
pstNode = NULL;
return;
}
修改如下:
VOID PT_WEB_DelUser(IN DLL_NODE_S *pstNode)
{
DLL_Delete(&g_stPtWebUserDll, pstNode);
pstUserInfo = (PT_WEB_USER_S *)DLL_GET_HANDLE(pstNode);
MEM_Free(pstUserInfo);
MEM_Free(pstNode);
return;
}
5、内存越界
① 字符串拷贝越界
ULONG CFM_ECF_BuildRun()
{
...
ulLen = (ULONG)strlen(szCmdBuf);
pcTmp = MEM_Malloc(MID_CFM|SID_CHAR, ulLen);
if(NULL == pcTmp)
{
return ERROR_FAILED;
}
strncpy(pcTmp, szCmdBuf, ulLen);
...
}
② 字符串拼装越界
VOID FTPS_StartUserTask(ULONG ulExecID, ULONG ulSocketID)
{
CHAR szTaskName[4];
ulIndex = CLI_VecterSet(g_pstFtpUserVector, (VOID*)lpFTPUSerData);
FTPS_InitFTPUserData(lpFTPUserData, lSocketID, ulExecID, ulIndex);
(VOID)snprintf(szTaskName, "FC%d", ulIndex); // ulIndex可能为-1,此时需要占用5个字节,超出了4
...
return;
}
③ strncpy拷贝长度使用错误;
CHAR szCertDomain[MAX_CERTDOMAIN_LEN + 1];
if(BOOL_TRUE != bUndoFlag)
{
strncpyl(szCertDomain, pcDomain, (ULONG)strlen(pcDomain)); // 此处应该以目的缓冲区为依据,(ULONG)strlen(pcDomain) 应该修改为MAX_CERTDOMAIN_LEN
}
④ 内存清零错误,导致未初始化内容;
ULONG TEL_ConvertCRLF()
{
CHAR *pcBuf = NULL;
ulStrLen = (ULONG) strlen(pszSourceStr);
pcBuf = (CHAR*)MEM_Malloc( MID_TEL_CLIENT, ulStrLen*2 + 1 );
if(NULL == pcBuf)
{
return ERROR_FAILED;
}
MEM_ZERO(pcBuf,sizeof(pcBuf)); // sizeof(pcBuf)为4,只清零了4个字节
}
⑤ 计算字符串长度使用sizeof;
#define SVPN_CGI_URL_PATH_HEAD_LEN (sizeof("/svpn.cn") - 1) // 此处sizeof应该修改为strlen
6、空指针/野指针
① 释放全局句柄资源后没有清理句柄本身;
(VOID)TWL_DeleteTwlInstance(g_ulAAATwlHandle);
g_ulAAATwlHandle = TWL_INVALID_HANDLE; // 此行不能遗漏
② 释放内存后没有清理数据结构上的指针;
ULONG WS_CM_Create()
{
pstLinkCtrl = (WS_CM_CTL_S *) MEM_Malloc(MID_WS_CM, sizeof(WS_CM_CTL_S));
...
g_astCtrl[ulIndex].pstCtrl = pstLinkCtrl;
DLL_Init_Node(&(pstLinkCtrl->stList), pstLinkCtrl);
if(...)
{
(VOID)MEMP_Free(pstLinkCtrl);
return ERROR_FAILED;
}
}
③ 访问空指针;
ULONG ALTIPv6_NumAddNode()
{
PACCESSLISTGROUP_IPv6_S pstGroup = NULL;
if(ulAclNum < ACL_BAS_MIN)
{
ulErrCode = ACL_ERROR_INVALID_NUM;
goto Exit;
}
...
Exit:
if(ACL6_SUCCESS == ulErrCode)
{
(VOID)ACL_IPv6_SendToDp(ulAclNum, ....);
}
ACLIPv6_SetGroupNextID(pstGroup);
return ulErrCode;
}
VOID ACLIPv6_SetGroupNextID(PACCESSLISTGROUP_IPv6_S *pstGroup)
{
switch(pstGroup->eGroupType)
{
......
}
}
7、未初始化
① 特定情况下对未初始化的结构变量的字段赋值,或者结构体成员初始化不完全;;
VOID PT_WEB_ProcWsConnInMsg()
{
WS_CM_ASYN_S stSYN;
if(CONNECTION_TYPE_SSL == pstNewConn->ulConnectType)
{
stSYN.ulEventID =SE_ASYNC;
}
stSYN.ulQueueID = g_ulPT_WebConAsyncQueMsgID;
stSYN.ulPointer = ulConnID;
stSYN.ulWakeTaskID = g_ulPT_MainTaskID;
}
② 函数的入参没有被初始化;
ULONG CFG_GetSlotIDFromMsg(...)
{
ULONG ulNoneSelfSlotMask;
...
(VOID)HA_SetMemberSlot(ulBoard, &ulNodeSelfSlotMask);
}
8、代码冗余
① 反复多重间接寻址访问数据,反复调用相同或类似的大段代码
② 临时使用小内存(<256字节),不需要申请动态内存, 可以使用局部变量
③ 结构体成员每个都赋值;
④ 拷贝常量字符串;
(VOID) Res_LoadString(...,&pszResInfo);
sprintf(szBuf,pszResInfo);
if(EXEC_DISPLAY_STOP == EXEC_OutStringWait(ulExecID, szBuf,&ulCurLine))
{
return ERROR_FAILED;
}
可以修改为如下
(VOID) Res_LoadString(...,&pcResInfo);
if(EXEC_DISPLAY_STOP == EXEC_OutStringWait(ulExecID, pcResInfo,&ulCurLine))
{
return ERROR_FAILED;
}
⑤ 没有必要为字符串申请静态的数据空间;
#define DP_NATPT_LOG_BUFFER_SIZE 512
枚举 DP_NATPT_MAXMUM 的值为61
CHAR g_szDPNatptDbgMsg[DP_NATPT_MAXMUM+1][DP_NATPT_LOG_BUFFER_SIZE]=
{
"The pointer to MBUF is null. \r\n";
"Failed to malloc memory for natpt,so drop the packet. \r\n";
"Failed to TR send \r\n";
...
}
上述全局变量 g_szDPNatptDbgMsg 占用的空间为 62 * 512 = 31K 字节
修改如下:
CHAR *g_apcDPNatptDbgMsg[DP_NATPT_MAXMUM+1]
{
"The pointer to MBUF is null. \r\n";
"Failed to malloc memory for natpt,so drop the packet. \r\n";
"Failed to TR send \r\n";
...
}
⑥ 反复计算循环条件
ULONG CFM_GetDividerNum(const UCHAR *pucFileName)
{
for(ulLoop = 0; ulLoop<(ULONG)strlen((CHAR*)pucFileName);; ulLoop++)
{
......
}
}
9、编程接口
模块接口或者驱动接口使用错误,导致功能或资源类问题异常
10、资源型接口设计
① 释放参数携带的资源时规则不一致:例如成功时释放,失败时不释放
② 复合资源的申请和释放没有封装 或申请释放函数封装不对称
③ 资源创建/获取型函数没有将资源作为返回值,而是作为输出参数
编程常见问题总结
浙公网安备 33010602011771号