我被分配到一个项目,这是一个用 C++ 和 ActiveX 编写的复杂遗留系统 ~ 10 岁。
设置是 Microsoft Visual Studio 2008。
虽然目前系统没有问题,但作为遗留系统安全审查的一部分,由于安全漏洞,自动安全代码扫描工具已将 realloc 实例标记为不良实践问题。
这是因为 realloc 函数可能会使敏感信息的副本滞留在内存中,无法被覆盖。该工具建议用 malloc、memcpy 和 free 替换 realloc。
现在 realloc 函数用途广泛,将在源缓冲区为空时分配内存。当缓冲区的大小为 0 时,它也会释放内存。我能够验证这两种情况。资料来源:MDSN 图书馆 2001
realloc 返回一个指向重新分配(并且可能移动)的内存块的 void 指针。如果大小为零且缓冲区参数不为 NULL,或者没有足够的可用内存将块扩展为给定大小,则返回值为 NULL。在第一种情况下,原始块被释放。第二,原块不变。返回值指向一个存储空间,该存储空间保证为存储任何类型的对象而适当对齐。要获取指向非 void 类型的指针,请对返回值使用类型转换。
所以,我使用 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 )
我编写了一个小型测试工具(控制台应用程序),它执行以下操作:
一个。添加 500000 个包含 2 个整数和一个 STL 字符串的类实例。
添加的整数正在运行计数器,其字符串表示形式如下:
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 行不同长度的大日志文件,添加到此数组的一个实例:CArray of CStrings 和 CArray of strings。
我使用现有方法(基线)和我修改的方法运行测试工具。我在调试和发布版本中都运行了它。
结果如下:
测试 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 行 CArray 的 CString 的大日志文件
原始实现:5 秒,修改后的实现:5 秒
测试 6:发布版本 -> 读取包含 86527 行 CArray 的 CString 的大日志文件
原始实现:5 秒,修改后的实现:5 秒
将大日志文件读入字符串对象的 CArray:
测试 7:调试构建 -> 读取包含 86527 行字符串 CArray 的大日志文件
原始实现:12 秒,修改后的实现:16 秒
测试 8:发布版本 -> 读取包含 86527 行字符串 CArray 的大日志文件
原始实现:9 秒,修改后的实现:13 秒
问题:
从上面的测试可以看出,realloc 始终比 memalloc、memcpy 和 free 更快。在某些情况下(例如,Test-2)它的速度快了 367%。同样,对于 Test-4,它是 234%。那么我能做些什么来降低这些与 realloc 实现相当的数字呢?
我的版本可以提高效率吗?
假设:
请注意,我不能使用 C++ new 和 delete。我必须只使用 malloc 和 free。我也无法更改任何其他方法(因为它是现有功能)并且影响很大。所以我的双手被绑住,以尽可能地获得 realloc 的最佳实现。
我已经验证我修改后的实现在功能上是正确的。
PS:这是我的第一个 SO 帖子。我试图尽可能详细。对张贴的建议也表示赞赏。