将未初始化的内存传递给函数时如何断言/测试

Posted

技术标签:

【中文标题】将未初始化的内存传递给函数时如何断言/测试【英文标题】:How to assert/test when uninitialised memory is passed to function 【发布时间】:2013-07-31 16:54:40 【问题描述】:

我有一种情况,有时发现我的部分代码传递了未初始化的内存。我正在寻找一种在使用调试堆运行时发生这种情况时可以断言的方法。这是一个可以在某些地方抛出的函数,以便在跟踪错误时提供额外帮助:

void foo( char* data, int dataBytes )

    assert( !hasUninitialisedData(data,dataBytes) ); //, This is what we would like
    ...

我看到有像 valgrind 这样的工具,当我在 Windows 上运行时,有 DrMemory。然而,这些在应用程序外部运行,因此当开发人员出现问题时不会发现问题。更重要的是,这会引发数千份关于 Qt 和其他无关函数的报告,让事情变得不可能。

我认为这个想法是有一个函数可以在数组中搜索 0xBAADFOOD,但存在一系列潜在的十六进制值,并且这些值会因平台而异。当存储整数时,这些十六进制值有时也可能有效,因此不确定是否可以从调试堆中获得更多信息。

我主要感兴趣的是可能存在 CRT 函数、库、visual-studio 断点或其他辅助函数来执行此类检查。 “感觉”好像应该已经有一个地方了,我还找不到它,所以如果有人对这种情况有一些好的解决方案,将不胜感激。

编辑:我应该解释得更好,我知道调试堆会用一个值初始化所有分配,以尝试检测未初始化的数据。如前所述,接收到的数据包含一些 0xBAADFOOD 值,通常内存是用 0xCDCDCDCD 初始化的,但这是分配数据的第三方库,显然有多个幻数,因此我很感兴趣是否在某处隐藏了通用检查。

【问题讨论】:

这是一个 catch 22 情况:如果不触发未定义的行为,就无法检查未初始化内存的内容,如果不检查内存内容,就无法确定内存是否已初始化。解决此问题的一种方法是要求您的 API 传递一个 struct/class 而不是缓冲区,并在您的 struct 中保留一个“已初始化”标记。不幸的是,这也不是万无一失的。 目的是能够检测导致未定义行为的未初始化内存。关于读取已初始化为 0xCD 的分配内存的问题绝不是未定义的行为。我已经更新了详细信息,因为这个内存是为第 3 方库提供的,并且它部分是未初始化的缓冲区,因为可能没有设置每 4 个整数。 【参考方案1】:

VC++ 运行时,至少在调试版本中,使用特定值初始化所有堆分配。只要我记得,它就一直是相同的值。但是,我不记得实际值。你可以做一个快速的分配测试和检查。

【讨论】:

【参考方案2】:

VC++ 程序的调试版本通常在启动时将未初始化的内存设置为0xCD。这在会话的生命周期内是不可靠的(一旦内存被分配/使用/释放,值就会改变),但它是一个开始的地方。

【讨论】:

【参考方案3】:

我现在已经实现了一个函数,它基本上可以在 wiki (Magic numbers) 上找到一个幻数列表后完成预期的操作:

/** Performs a check for potentially unintiialised data
    \remarks May incorrectly report uninitialised data as it is always possible the contained data may match the magic numbers in rare circumstances so this function should be used for initial identification of uninitialised data only
*/
bool hasUninitialisedData( const char* data, size_t lenData )

    const unsigned int kUninitialisedMagic[] = 
    
        0xABABABAB, // Used by Microsoft's HeapAlloc() to mark "no man's land" guard bytes after allocated heap memory
        0xABADCAFE, // A startup to this value to initialize all free memory to catch errant pointers
        0xBAADF00D, // Used by Microsoft's LocalAlloc(LMEM_FIXED) to mark uninitialised allocated heap memory
        0xBADCAB1E, // Error Code returned to the Microsoft eVC debugger when connection is severed to the debugger
        0xBEEFCACE, // Used by Microsoft .NET as a magic number in resource files
        0xCCCCCCCC, // Used by Microsoft's C++ debugging runtime library to mark uninitialised stack memory
        0xCDCDCDCD, // Used by Microsoft's C++ debugging runtime library to mark uninitialised heap memory
        0xDEADDEAD, // A Microsoft Windows STOP Error code used when the user manually initiates the crash.
        0xFDFDFDFD, // Used by Microsoft's C++ debugging heap to mark "no man's land" guard bytes before and after allocated heap memory
        0xFEEEFEEE, // Used by Microsoft's HeapFree() to mark freed heap memory
    ;
    const unsigned int kUninitialisedMagicCount = sizeof(kUninitialisedMagic)/sizeof(kUninitialisedMagic[0]);

    if ( lenData < 4 ) return assert(false=="not enough data for checks!"), false;

    for ( unsigned int i =0; i < lenData - 4; ++i ) //< we don't check the last few bytes as keep to full 4-byte/int checks for now,  this is where the -4 comes in
    
        for ( unsigned int iMagic = 0; iMagic < kUninitialisedMagicCount; ++iMagic )
        
            const unsigned int* ival = reinterpret_cast<const unsigned int*>(data + i);
            if ( *ival == kUninitialisedMagic[iMagic] )
                return true;
        
    
    return false;

【讨论】:

我仍然希望有一个 Microsoft 或 CRT 函数来执行类似的操作或执行幻数检查,除非这是唯一/最好的方法?

以上是关于将未初始化的内存传递给函数时如何断言/测试的主要内容,如果未能解决你的问题,请参考以下文章

为何将未被初始化的Integer变量赋值给int变量时会抛出空指针异常?

为何将未被初始化的Integer变量赋值给int变量时会抛出空指针异常?

请问使用Jmeter进行自动化脚本设计时经常使用哪些元件?

将 cclayer 传递给我的对象时的内存问题

10.Android UiAutomator Junit 断言函数的使用

go基础笔记