1 现象:问题描述
一个CBufferStream类封装了流的读写处理,负责往一个buffer中写入和读取多个变长数据.
写入时会预先判断buffer的剩余空间,当不够时,会realloc双倍的空间.每次realloc之后就会出现越界写的情况,并导致coredump.
2 关键过程:根本原因分析
TInt4 CBufferedStream::write(const TChar* buff, TUInt4 len)
{
//begin add by xiechuan
TUInt4 written = 0;
TChar* pTemp = NULL;
if (m_currLen+ len> m_currBufferSize)//如果即将写满
{
//重新分配buffer大小
m_currBufferSize = (m_currLen+ len> m_currBufferSize*2) ?(m_currLen+ len):(m_currBufferSize*2);
pTemp = (TChar*)realloc(*m_pBufferStart,m_currBufferSize);
if(!pTemp)//如果为空则返回-1
{
return -1;
}
*m_pBufferStart = pTemp;//将改变后的首地址保存
m_pBuffer = pTemp + m_offSet;//重新计算当前的buffer
}
memcpy(m_pBuffer+m_currWritePos,buff,len);
m_currWritePos+=len;
m_currLen += len;
return 0;
}
Buffer的布局:
M
m_pBufferStart m_pBuffer
由于buffer前面一部分是有消息头数据的,所以变长数据的写入只能从m_pBufferStart+offset开始.当buffer不够时将m_currBufferSize的长度翻倍后,然后realloc.
m_currBufferSize = (m_currLen+ len> m_currBufferSize*2) ?(m_currLen+ len):(m_currBufferSize*2);
pTemp = (TChar*)realloc(*m_pBufferStart,m_currBufferSize);
由于重新分配内存是从m_pBufferStart开始的,而没有加上消息头的长度(offset).所以导致realloc之后,真正增加的buffer长度并不是先前预计的2倍.导致后续继续写时.越界.
3 结论:解决方案及效果
pTemp = (TChar*)realloc(*m_pBufferStart,m_currBufferSize+m_offset);
重新分配内存时应该加上前面消息头的长度.修改之后无越界和coredump.
4 经验总结:预防措施和规范建议
1.内存操作要注意长度越界的问题.最好画出内存布局,把边界考虑清楚再编码.
5 备注
6 考核点
对c内存分配函数的了解和内存操作的越界错误的反应
7 试题
1.有一段连续内存buff,前面存放的是固定消息头,长度为head_length,后面存放的是可变数据长度为data_length.往内存可变数据区写入数据,长度不够时(算法是:即将写入的长度 + 已经写入的长度 > data_length)下列处理最合理的是:
A. data_length*=2;
buff = (char*)malloc(data_length);
B. data_length*=2;
Char* tmp = (char*)realloc(buff,data_length+head_length);
buff = tmp;
C. data_length*=2;
buff = (char*)malloc(data_length+head_length);
D. data_length*=2;
Char* tmp = (char*)realloc(buff ,data_length);
buff = tmp;
答案:B
A,C错误原因是,重新分配内存,造成原来的数据丢失和内存泄漏
D.错误原因是,realloc时没有加上消息头长度,重新分配的内存一部分用来存放消息头,导致可变数据区的真实大小 < data_length.而上面指定的判断长度的算法就可能产生越界.
阅读(815) | 评论(0) | 转发(0) |