低级错误案例集汇总.docx
- 文档编号:24612550
- 上传时间:2023-05-29
- 格式:DOCX
- 页数:72
- 大小:46.13KB
低级错误案例集汇总.docx
《低级错误案例集汇总.docx》由会员分享,可在线阅读,更多相关《低级错误案例集汇总.docx(72页珍藏版)》请在冰豆网上搜索。
低级错误案例集汇总
TOP1资源泄漏
资源泄漏(包括内存泄漏)是代码Review中最常见的错误之一,申请的每个资源必须明确由谁负责释放,何时释放,在何处释放;在异常/错误/返回处理中,保持清醒的头脑,清理战场。
此处的资源还包括信号量、定时器、文件句柄等系统资源。
案例1.1
【问题描述】
宏里面有return语句导致内存泄漏案例一。
【问题分析】
1)错误代码:
/*定义宏MODEL_ASSERT_RETFAIL*/
#defineMODEL_ASSERT_RETFAIL(X)\
{\
if(X不合法)\
return;\
}
….//dosomething
MDSTrafficMsg*pMsg=VOS_AllocMsg(PID_MD,usLength);
if(NULL_PTR==pMsg)
{
return;
}
MDSDataListenerMgr*pDataListener=MDSDataInitalListenerMgr();
MODEL_ASSERT_RETFAIL(pDataListener);
2)分析:
使用宏MODEL_ASSERT_RETFAIL检查pDataListener是否合法,如果不合法,则直接返回,一旦返回,将导致前面通过指针pMsg申请到的消息包资源泄漏。
【纠正方法】
在宏MODEL_ASSERT_RETFAIL分支判断return前加上VOS_FreeMsg(PID_MD,pMsg):
该方法代码不够清晰,当用户看宏定义时,对VOS_FreeMsg(PID_MD,pMsg)不清楚还要跳回来看前面的代码。
设定该宏有返回值(指针不为空返回VOS_True,否则为VOS_False),将宏的return语句写在宏使用后(判断指针pDataListener合法性),若宏返回VOS_False释放pMsg并返回主调函数:
该方法在遇到只判断一个指针的合法性时,浪费代码行、降低代码飞检效率且可能存在宏描述歧义等问题,简单的判断建议不使用宏。
【经验教训】
在XX版本的一个新模块的开发中,在发布之前进行大话务量测试验证时,发现系统内存资源不足,当时发布在即,经过协调多个技术专家封闭攻关,花了三天时间终于发现问题所在,人力成本高达3000元,对于内存使用,要确保释放闭环,所有异常退出点都需要释放内存。
案例1.2
【问题描述】
宏里面有return语句导致内存泄漏案例二。
【问题分析】
1)错误代码:
头文件中的宏定义如下:
#defineNODE_RETURN_ERROR(p)\
{\
if(NULL==p)\
{\
VOS_RECORD_ERROR(p);\
returnNULL;\
}\
}
文件中有个函数有如下代码段:
...//dosomething
pNode=(Node_Head_S*)malloc(sizeof(Node_Head_S));
NODE_RETURN_ERROR(pNode);//第一次使用宏
pBody=(Node_Body_S*)malloc(sizeof(Node_Body_S));
NODE_RETURN_ERROR(pBody);//第二次使用宏
...//dosomething
2)分析:
当通过指针pBody申请内存,然后通过宏NODE_RETURN_ERROR来判断是否申请成功,如果申请失败,则在宏NODE_RETURN_ERROR里面就直接返回了,这样导致通过指针pNode申请的内存泄漏了。
【纠正方法】
不使用宏,或将宏中的return语句写到宏调用后。
案例1.3
【问题描述】
异常出口没有释放应该释放的内存案例一。
【问题分析】
1)错误代码:
//GetBuff函数的作用是申请动态内存
pMsgDB_DEV=(PDBDevMsg)GetBuff(sizeof(DBDevMsg),__LINE__);
if(NULL==pMsgDB_DEV)
{
return;
}
//GetBuff函数的作用是申请动态内存
pMsgDBApp_To_Logic=(LPDBSelfMsg)GetBuff(sizeof(DBSelfMsg),__LINE__);
if(NULL==pMsgDBApp_To_Logic)
{
return;
}
2)分析:
在第2个return处,pMsgDB_DEV指向的内存丢失。
【纠正方法】
在第2个return处增加释放内存的操作。
【经验教训】
函数中有动态申请内存,要在函数范围内检查所有return语句是否释放该return语句前所有动态申请的内存。
案例1.4
【问题描述】
申请过的内存的指针没有释放,又申请新的内存给它。
【问题分析】
1)错误代码:
/*申请新的内存大小*/
pTmp=VOS_Malloc(pMacroEdit->ulMacroLen+1);
if(NULL==pTmp)
{
returnFAILURE;
}
VOS_MemSet(pTmp,'\0',pMacroEdit->ulMacroLen+1);
......
/*又申请新的内存*/
pTmp=VOS_Malloc(sizeof(TTTT_RPC_MSG_S));
if(NULL==pTmp)
{
returnFAILURE;
}
…….
VOS_Free(pTmp);
2)分析:
第一次申请的内存没有释放就使用同一个指针又申请了内存。
这样第一次申请的内存就永远没办法释放了,造成了内存泄漏。
【纠正方法】
第2次申请内存前,先释放掉第1次申请的内存。
同时我们应该注意到这个案例中对于申请的内存使用的指针变量是个临时变量,其命名没有十分明确的承载其所指向内存的含义,这样命名是不规范的,也许恰恰就是因为这点,才引发了后面继续直接使用.
案例1.5
【问题描述】
异常出口没有释放应该释放的资源。
【问题分析】
1)错误代码:
intr_lock();
/*填充消息*/
ulResult=DEV_MA_FillMsg(pMsg,ucPrimID,usBIndex,ucSerailID);
if(ulResult!
=MSP_RETURN_NO_ERR)
{
VOS_FreeMsg(PID_DEV,pMsg);
returnulResult;
}
intr_unlock();
……
2)分析:
return前没有调用intr_unlock()释放中断信号量。
【纠正方法】
return前释放中断信号量。
案例1.6
【问题描述】
分支考虑不全,导致某些分支中内存泄漏。
【问题分析】
1)错误代码:
/*创建消息,并放入消息队列中,假设处理成功*/
pMsg=CreateQueueMsg(j);
mu=SortOperation(pMsg);
if(mu<=0){
ret=0;
}
elseif(mu<=5){
ret=SendMsgToMu(pMsg,mu-1);
}
if(ret<=0){
/*从队列中删除消息,并释放*/
RemoveQueueMsg(j);
}
2)分析:
mu>5的分支,根本没有考虑,内存泄漏。
【纠正方法】
增加mu>5时对内存的释放处理。
案例1.7
【问题描述】
多个判断放在一起进行导致内存泄漏。
【问题分析】
1)错误代码:
char*pszInfoBuf1=VOS_NULLPTR;
char*pszInfoBuf2=VOS_NULLPTR;
pszInfoBuf1=(char*)VOS_Malloc(MID_BVLAN,ulBuffLen);
pszInfoBuf2=(char*)VOS_Malloc(MID_BVLAN,ulBuffLen);
if((pszInfoBuf1==NULL)||(pszInfoBuf2==NULL))
{
returnVOS_ERR;
}
2)分析:
当pszInfoBuf1申请成功,但pszInfoBuf2申请失败时,if语句被执行,pszInfoBuf1指向的内存泄漏了。
【纠正方法】
将两处申请内存后的有效性判断分开进行。
案例1.8
【问题描述】
内存重复释放可能产生不可预知的后果。
【问题分析】
1)错误代码:
voidNbvmConfirmBrdVer(NBOM_TRANS*pstTrans)
{
…………
if(NULL!
=pstTrans->pbDynMem)
{
pstSelfBoardBootRomInfo=(NBVM_SELF_BOARD_BOOTROM_INFO*)(void*)pstTrans->pbDynMem;
}
NBVM_MEM_FREE(pstSelfBoardBootRomInfo);
NBVM_MEM_FREE(pstBrdAllSwInfo);
NBVM_MEM_FREE(pstTrans->pbDynMem);
NBVM_MEM_FREE(pstTrans->pPrivatePtr);
}
2)分析:
上述红色代码造成了pstTrans->pbDynMem所指向的内存重复释放。
【纠正方法】
内存重复释放可能产生不可预知的后果:
假如任务A申请了内存块M,使用后释放M;系统可能把空闲的M分配给了任务B,当任务A重复释放M后,把本来属于任务B的该内存块误释放;此时系统认为空闲的内存块M又可能分配给任务C,C随后对这个内存块的写操作对于任务B来说就是非法操作,可能导致任务B运行异常。
案例1.9
【问题描述】
系统运行过程中,出现堆内存不足。
【问题分析】
1)错误代码
typedefstructtagWordStat{
char*pszWord;
ULONGulSum;
floatfrate;
structtagWordStat*psnext;
}WORDSTAT_S,*PWORDSTAT_S;
…
psWordName=(char*)malloc(ulCurWordLen+1);
if(NULL==psWordName)
{
returnVOS_ERR;
}
/*获取字符串*/
VOS_strncpy(psWordName,strName,ulCurWordLen);
...
/*建立新节点*/
pstWordStat=(PWORDSTAT_S)VOS_Malloc(sizeof(WORDSTAT_S));
if(NULL==pstWordStat)
{
VOS_Free(psWordName);
returnSTAT_ERR;
}
memset(pstWordStat,0,sizeof(WORDSTAT_S));
pstWordStat->pszWord=psWordName;
psWordName=NULL;
pstWordStat->ulSum=1;
pstWordStat->psnext=NULL;
ulTotalWordNum++;
...
/*释放节点*/
VOS_Free(pstWordStat);
...
2)分析
在释放节点时,只释放申请的部分内存。
在这段代码中,字符串存放申请了一段内存,建立链表的节点又申请了内存,最后只释放了链表节点内存,没有释放字符串存放时申请的内存。
【纠正方法】
先释放结构体内指针成员申请的内存,再释放结构体指针指向的内存。
释放结构体节点内存时,一定要搞清楚节点内部是否还有需要释放的内存,否则会导致这段内存永远得不到释放。
TOP2内存越界
所谓内存越界就是申请了内存,使用时超出了申请的范围。
发生内存越界的情况,往往出错的地方不是真正内存越界的地方,带来的影响滞后,而且难以定位,需要程序员养成良好编程习惯,对指针,内存拷贝,字符串操作等注意前后空间的变化,可以做一些必要保护。
案例2.1
【问题描述】
数组下标访问越界。
【问题分析】
1)错误代码:
/*初始化Q922的I帧链路索引地址表*/
ulSize=sizeof(INDEX_Q922IFRAME_MAPPING_STRU**)*DCOM_MAXSERVICEQ922LINK;
//g_ppstQ922IFrameIndex[]申请的长度是DCOM_MAXSERVICEQ922LINK
g_ppstQ922IFrameIndex=(INDEX_Q922IFRAME_MAPPING_STRU**)VOS_MemAlloc(VOS_PID_COMM,STATIC_DOPRA_MEM_PT,ulSize);
...
VOS_UINT32Q922_AddLink(......)
{
...
if(usLinkNo>DCOM_MAXSERVICEQ922LINK)
{
ulRet=VOS_ERRNO_Q922CFG_ADDLINK_LINKNO_INVALID;
returnulRet;
}
/*初始化Q922的I帧映射表的信息*/
ulSize=sizeof(INDEX_Q922IFRAME_MAPPING_STRU);
g_ppstQ922IFrameIndex[usLinkNo]=(INDEX_Q922IFRAME_MAPPING_STRU*)VOS_MemAlloc(VOS_PID_COMM,
DYNAMIC_DOPRA_MEM_PT,ulSize);
if(VOS_NULL_PTR==g_ppstQ922IFrameIndex[usLinkNo])
{
.....
ulRet=VOS_ERRNO_Q922CFG_INIT_ALLOC_IFRAMEMAP_FAILED;
returnulRet;
}
...
}
2)分析:
造成内存写越界,单板复位。
【纠正方法】
内存申请最大长度是DCOM_MAXSERVICEQ922LINK,最多访问到g_ppstQ922IFrameIndex[DCOM_MAXSERVICEQ922LINK-1],判断应改成if(usLinkNo>=DCOM_MAXSERVICEQ922LINK)
案例2.2
【问题描述】
版本倒换,主控板一起来就出现指令异常,单板复位
【问题分析】
1)错误代码:
CHARsLogInfo[1024];
UCHARszTmp[10];/*只分配了10个字节*/
char*pTemp=sLogInfo;
…….
while(*pTemp!
='-')
{
szTmp[i]=*pTemp;/*没有找到-,继续进行,导致堆栈被写坏*/
pTemp++;
i++;
}
szTmp[i]=0;
2)分析:
当数组下标i大于等于9时,不能再继续循环,否则越界访问。
【纠正方法】
增加判断:
i等于8时,跳出循环,对于while或者for循环里操作数组下标要做下标的保护,“小心驶得万年船”
案例2.3
【问题描述】
入参检测失败后不退出函数,仍继续执行后面逻辑处理,导致后面使用数组下标越界,在异常引用情况下导致死机。
【问题分析】
1)错误代码:
voidFARCheck_ImsDataTab_by_timer(CRims_data_tab_cr)
{
……
if((ims_data_tab_cr)>=MAX_IMS_DATA_CR)——A
{
CCB_stop_timer_by_ftr(ims_data_tab_cr,EN_CCB_FTR_ID_RF,EN_RF_CHECK_SINGLE_IMSDATA_TIMER);—>后面需要return,退出函数
}
if((MAX_SIPSL_CR==g_ims_data_table[ims_data_tab_cr].sip_cr)——B
&&(MAX_CCB(LOCAL_MODULE)==g_ims_data_table[ims_data_tab_cr].ccb_cr))
{
……
return;
}
……
}
2)分析:
当A处的ims_data_tab_cr入参检查失败后不退出而继续进行下面的逻辑处理,导致B处引用ims_data_tab_cr作为数组下标越界
【纠正方法】
在入参检测中失败应做退出函数处理,A处逻辑判断中增加return
案例2.4
【问题描述】
系统运行过程中突然复位。
【问题分析】
1)错误代码:
while(_isspace(*szString))
{
/*跳过所有空格字符*/
szString++;//szString为函数的输入参数
}
if('\0'==*szString)
{
returnFAILURE;
}
/*'='的左边变量赋值*/
szStr=VOS_Malloc(20);//申请内存大小为给20
if(NULL==szStr)
{
returnMACRO_MEM_SHORTAGE;
}
VOS_MemSet(szStr,'\0',20);
VOS_StrCpy(szStr,szString);
2)分析:
这种内存写越界问题的表面现象是不确定的。
该问题的原因就是没有检查szString字符串的长度,最后一代码中,字符串拷贝时发生写越界。
【纠正方法】
规避这种问题的办法是拷贝字符串是采用DOPRA平台提供的安全拷贝函数
CHAR*VOS_strncpy(CHAR*dst,constCHAR*src,ULONGn)。
案例2.5
【问题描述】
Sprintf使用不当导致内存访问越界
【问题分析】
1)错误代码:
charpszInfoBuf[250];
sprintf(pszInfoBuf,”***File:
%sLine:
%d****”,__FILE__,__LINE__);
2)分析:
“__FILE__”在预编译时,被编译时的目录名和源文件名代替,但目录和文件名的长度可变,很可能超出250个字节,导致内存越界
【纠正方法】
将sprintf替换成安全函数snprintf,指定缓冲区大小INFOBUF_SIZE,确保内存不会越界
snprintf(pszInfoBuf,INFOBUF_SIZE-1,”***File:
%sLine:
%d****”,__FILE__,__LINE__);
pszInfoBuf[INFOBUF_SIZE-1]=‘\0’;
【经验教训】
C语言提供的字符串库函数sprintf/vsprintf/strcpy/strcat/gets等非常危险,很容易导致内存越界,应该使用安全的字符串库函数snprintf/strncpy/strncat/fgets,指定操作的内存大小。
对C++语言,应该使用相应的类库如std:
:
string,std:
:
stringstream,boost:
:
lexical_cast操作字符串,而不要直接调用C语言的字符串函数。
案例2.6
【问题描述】
Strcpy使用不当导致内存访问越界。
【问题分析】
1)错误代码:
charpszInfoBuf[32];
strcpy(pszInfoBuf,pMsg);
2)分析:
定义的pszInfoBuf共32字节,pMsg是从网络上接收的数据包,可能超出32个字节,导致内存越界
【纠正方法】
将strcpy替换成安全函数strncpy,指定缓冲区大小INFOBUF_SIZE,确保内存不会越界
strncpy(pszInfoBuf,pMsg,INFOBUF_SIZE-1);
pszInfoBuf[INFOBUF_SIZE-1]=‘\0’;
案例2.7
【问题描述】
字符串拼接,忘记后面的\0.
【问题分析】
1)错误代码:
ulNewSize=VOS_strlen(“\r\n”)+VOS_strlen(pRevData)+VOS_strlen(pszVlanInfo)+VOS_strlen(pszQinQinfo);
pszDisInfo=VOS_Malloc(MID_BVLAN,ulNewSize);
......//拷贝一些内容给pszDisInfo
VOS_strcat(pszDisInfo,pszQinQinfo);//字符串连接
2)分析:
以ulNewSize为长度申请内存,但忘记字符串结尾必须有一个’\0’,调用strcat时拷贝了字符串本身和最后的’\0’,导致内存越界
【纠正方法】
计算ulNewSize时,增加1个字节,用于存放’\0’.
案例2.8
【问题描述】
数组下标越界。
【问题分析】
1)错误代码:
//pstPSCCfgInfo->strPara.ucNumOfCID超过了数组下标的最大值。
for(ulCount=0;ulCount
{
pPSCTable->strPara.stCidInfo[ulCount].bitCid=pstPSCCfgInfo->strPara.stCidInfo[ulCount].bitCid;
}
2)分析:
外部配入的pstPSCCfgInfo->strPara.ucNumOfCID值超过了定义的最大值SLP_MAX_PSC_CID_NUM。
由于代码未加保护,导致数组下标越界。
致BBI单板复位。
【纠正方法】
增加判断:
if(SLP_MAX_PSC_CID_NUM
{
pstPSCCfgInfo->strPara.ucNumOfCID=SLP_MAX_PSC_CID_NUM;
}
【经验教训】
对于外部输入的数组下标要作检查,避免内存越界
案例2.9
【问题描述】
整形转换成字符串时,没有注意到\0结尾。
【问题分析】
1)错误代码:
//使用itoa()将整型数转换为字符串时:
charszTempShold[10];
itoa(ulProcFrecy,szTempShold,10);
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 低级 错误 案例 汇总