我被分配到一个项目,这是一个复杂的遗留系统,用C和ActiveX编写,大约10年.
该设置是Microsoft Visual Studio 2008.
虽然系统目前没有任何问题,但作为遗留系统安全审查的一部分,由于安全漏洞,自动安全代码扫描工具已将重新分配的实例标记为“不良实践”问题.
这是因为realloc函数可能会将敏感信息的副本留在内存中,而不会被覆盖.该工具建议用malloc,memcpy和free替换realloc.
现在realloc函数是通用的,当源缓冲区为空时将分配内存.当缓冲区的大小为0时,它还释放内存.我能够验证这两种情况.
资料来源:MDSN图书馆2001
realloc returns a void pointer to the reallocated (and possibly moved) memory block. The return value is NULL if the size is zero and the buffer argument is not NULL, or if there is not enough available memory to expand the block to the given size. In the first case, the original block is freed. In the second, the original block is unchanged. The return value points to a storage space that is guaranteed to be suitably aligned for storage of any type of object. To get a pointer to a type other than void, use a type cast on the return value.
因此,我使用malloc,memcpy和free的替换函数必须满足这些情况.
我已经在原始代码片段(数组实现)下面再现了使用realloc动态调整大小和缩小其内部缓冲区的代码片段.
首先是类定义:
template <class ITEM>
class CArray
{
// Data members:
protected:
ITEM *pList;
int iAllocUnit;
int iAlloc;
int iCount;
public:
CArray() : iAllocUnit(30), iAlloc(0), iCount(0), pList(NULL)
{
}
virtual ~CArray()
{
Clear(); //Invokes SetCount(0) which destructs objects and then calls ReAlloc
}
现有的ReAlloc方法:
void ReAllocOld()
{
int iOldAlloc = iAlloc;
// work out new size
if (iCount == 0)
iAlloc = 0;
else
iAlloc = ((int)((float)iCount / (float)iAllocUnit) + 1) * iAllocUnit;
// reallocate
if (iOldAlloc != iAlloc)
{
pList = (ITEM *)realloc(pList, sizeof(ITEM) * iAlloc);
}
}
以下是我用malloc,memcpy和free替换它们的实现:
void ReAllocNew()
{
int iOldAlloc = iAlloc;
// work out new size
if (iCount == 0)
iAlloc = 0;
else
iAlloc = ((int)((float)iCount / (float)iAllocUnit) + 1) * iAllocUnit;
// reallocate
if (iOldAlloc != iAlloc)
{
size_t iAllocSize = sizeof(ITEM) * iAlloc;
if(iAllocSize == 0)
{
free(pList); /* Free original buffer and return */
}
else
{
ITEM *tempList = (ITEM *) malloc(iAllocSize); /* Allocate temporary buffer */
if (tempList == NULL) /* Memory allocation failed, throw error */
{
free(pList);
ATLTRACE(_T("(CArray: Memory could not allocated. malloc failed.) "));
throw CAtlException(E_OUTOFMEMORY);
}
if(pList == NULL) /* This is the first request to allocate memory to pList */
{
pList = tempList; /* assign newly allocated buffer to pList and return */
}
else
{
size_t iOldAllocSize = sizeof(ITEM) * iOldAlloc; /* Allocation size before this request */
size_t iMemCpySize = min(iOldAllocSize, iAllocSize); /* Allocation size for current request */
if(iMemCpySize > 0)
{
/* MemCpy only upto the smaller of the sizes, since this could be request to shrink or grow */
/* If this is a request to grow, copying iAllocSize will result in an access violation */
/* If this is a request to shrink, copying iOldAllocSize will result in an access violation */
memcpy(tempList, pList, iMemCpySize); /* MemCpy returns tempList as return value, thus can be omitted */
free(pList); /* Free old buffer */
pList = tempList; /* Assign newly allocated buffer and return */
}
}
}
}
}
笔记:
>在旧代码和新代码中正确构造和破坏对象.
>未检测到内存泄漏(由CRT调试堆函数中内置的Visual Studio报告:http://msdn.microsoft.com/en-us/library/e5ewb1h3(v=vs.90).aspx)
>我写了一个小测试工具(控制台应用程序),它执行以下操作:
一个.添加包含2个整数和STL字符串的500000个实例.
添加的整数正在运行计数器,其字符串表示如下:
for(int i = 0; i < cItemsToAdd; i++)
{
ostringstream str;
str << "x=" << 1+i << "\ty=" << cItemsToAdd-i << endl;
TestArray value(1+i, cItemsToAdd-i, str.str());
array.Append(&value);
}
湾打开一个包含86526行不同长度的大日志文件,添加到此数组的实例:CStrings的CArray和字符串的CArray.
我使用现有方法(基线)和我修改过的方法运行测试工具.我在调试和发布版本中运行它.
以下是结果:
测试1:调试构建 – >使用int,int,string,100000实例添加类:
原始实现:5秒,修改实现:12秒
测试2:调试构建 – >使用int,int,string,500000实例添加类:
原始实现:71秒,修改后的实现:332秒
测试3:发布版本 – >使用int,int,string,100000实例添加类:
原始实现:2秒,修改实现:7秒
测试4:发布版本 – >使用int,int,string,500000实例添加类:
原始实现:54秒,修改执行:183秒
将大日志文件读入CString对象的CArray:
测试5:调试构建 – >用86527行CSrray读取大日志文件
原始实现:5秒,修改执行:5秒
测试-6:发布版本 – >用86527行CSrray读取大日志文件
原始实现:5秒,修改执行:5秒
将大日志文件读入字符串对象的CArray:
测试7:调试构建 – >用86527行CArray字符串读取大日志文件
原始实现:12秒,修改执行:16秒
测试-8:发布版本 – >用86527行CArray字符串读取大日志文件
原始实现:9秒,修改执行:13秒
问题:
>从上面的测试中可以看出,与memalloc,memcpy和free相比,realloc始终更快.在某些情况下(例如,测试-2),它的速度快了367%.同样,对于Test-4,它是234%.那么我能做些什么来降低这些数字与realloc实现相当呢?
>我的版本可以提高效率吗?
假设:
>请注意,我不能使用C new并删除.我只能使用malloc和free.我也无法改变任何其他方法(因为它是现有功能)并且影响巨大.因此,我的双手必须得到最好的realloc实现.
>我已经验证我的修改后的实现在功能上是正确的.
PS:这是我的第一篇SO帖子.我试图尽可能详细.关于发布的建议也很感激.
解决方法:
首先,我想指出你没有解决这个漏洞,因为free释放的内存也没有被清除,就像realloc一样.
另请注意,您的代码比旧的realloc更多:它在内存不足时抛出异常.这可能是徒劳的.
为什么你的代码比realloc慢?可能是因为realloc正在使用引擎盖下的快捷方式,而这些快捷方式是您无法使用的.例如,realloc可能会分配比实际请求更多的内存,或者在前一个块结束后分配连续内存,因此您的代码比realloc执行更多的memcpy.
以防万一.在CompileOnline中运行以下代码会产生结果Wow no copy
#include <iostream>
#include <stdlib.h>
using namespace std;
int main()
{
void* p = realloc(NULL, 1024);
void* t = realloc(p, 2048);
if (p == t)
cout << "Wow no copy" << endl;
else
cout << "Alas, a copy" << endl;
return 0;
}
你能做些什么来加快你的代码?
你可以try to allocate more memory after the currently allocated block,但随后释放内存会变得更加困难,因为你需要记住你分配的所有指针,或者找到一种方法来修改free使用的查找表,一次性释放正确的内存量.
要么
使用(内部)分配两倍于先前分配的内存的公共策略,并且(可选地)仅在新阈值小于分配的内存的一半时缩小内存.
这给你一些头部空间,所以不是每次内存增长都需要调用malloc / memcpy / free.