何时以及为什么操作系统将内存初始化为malloc / free / new / delete上的0xCD,0xDD等?
我知道操作系统有时会用某些模式(如0xCD和0xDD)初始化内存。 我想知道的是什么时候以及为什么发生这种情况。
什么时候
这是特定于编译器使用?
做malloc /新的和释放/删除相同的方式在这方面的工作?
它是特定于平台吗?
它会发生在其他操作系统,如Linux或VxWorks?
为什么
我的理解是,这只发生在Win32debuggingconfiguration中,它用于检测内存溢出并帮助编译器捕获exception。
你能举出一些实际的例子来说明这个初始化是有用的吗?
我记得读过一些东西(也许在Code Complete 2中),分配内存到一个已知的模式是很好的,某些模式会在Win32中触发中断,这将导致在debugging器中显示exception。
这是多么便携?
编译为debugging模式时,微软编译器对各种无主/未初始化内存的使用情况的快速总结(支持因编译器版本而异):
Value Name Description ------ -------- ------------------------- 0xCD Clean Memory Allocated memory via malloc or new but never written by the application. 0xDD Dead Memory Memory that has been released with delete or free. Used to detect writing through dangling pointers. 0xED or Aligned Fence 'No man's land' for aligned allocations. Using a 0xBD different value here than 0xFD allows the runtime to detect not only writing outside the allocation, but to also detect mixing alignment-specific allocation/deallocation routines with the regular ones. 0xFD Fence Memory Also known as "no mans land." This is used to wrap the allocated memory (surrounding it with a fence) and is used to detect indexing arrays out of bounds or other accesses (especially writes) past the end (or start) of an allocated block. 0xFD or Buffer slack Used to fill slack space in some memory buffers 0xFE (unused parts of `std::string` or the user buffer passed to `fread()`). 0xFD is used in VS 2005 (maybe some prior versions, too), 0xFE is used in VS 2008 and later. 0xCC When the code is compiled with the /GZ option, uninitialized variables are automatically assigned to this value (at byte level). // the following magic values are done by the OS, not the C runtime: 0xAB (Allocated Block?) Memory allocated by LocalAlloc(). 0xBAADF00D Bad Food Memory allocated by LocalAlloc() with LMEM_FIXED,but not yet written to. 0xFEEEFEEE OS fill heap memory, which was marked for usage, but wasn't allocated by HeapAlloc() or LocalAlloc(). Or that memory just has been freed by HeapFree().
免责声明:表是从我躺在一些笔记 – 他们可能不是100%正确(或一致)。
这些值中的许多值是在vc / crt / src / dbgheap.c中定义的:
/* * The following values are non-zero, constant, odd, large, and atypical * Non-zero values help find bugs assuming zero filled data. * Constant values are good so that memory filling is deterministic * (to help make bugs reproducable). Of course it is bad if * the constant filling of weird values masks a bug. * Mathematically odd numbers are good for finding bugs assuming a cleared * lower bit. * Large numbers (byte values at least) are less typical, and are good * at finding bad addresses. * Atypical values (ie not too often) are good since they typically * cause early detection in code. * For the case of no-man's land and free blocks, if you store to any * of these locations, the memory integrity checker will detect it. * * _bAlignLandFill has been changed from 0xBD to 0xED, to ensure that * 4 bytes of that (0xEDEDEDED) would give an inaccessible address under 3gb. */ static unsigned char _bNoMansLandFill = 0xFD; /* fill no-man's land with this */ static unsigned char _bAlignLandFill = 0xED; /* fill no-man's land for aligned routines */ static unsigned char _bDeadLandFill = 0xDD; /* fill free objects with this */ static unsigned char _bCleanLandFill = 0xCD; /* fill new objects with this */
也有几次debugging运行时会用已知的值填充缓冲区(或缓冲区的一部分),例如std::string
分配中的“松弛”空间或传递给fread()
的缓冲区。 这些情况使用一个名为_SECURECRT_FILL_BUFFER_PATTERN
的值(在crtdefs.h
定义)。 我不确定它是什么时候推出的,但至less在VS 2005(VC ++ 8)中是在debugging运行时。
最初用于填充这些缓冲区的值是0xFD
与无人区域相同的值。 但是,在VS 2008(VC ++ 9)中,该值已更改为0xFE
。 我认为这是因为可能会出现填充操作超过缓冲区末尾的情况,例如,如果调用者传入的缓冲区大小太大而无法使用fread()
。 在这种情况下,值0xFD
可能不会触发检测到这个溢出,因为如果缓冲区大小只有一个,填充值将与用于初始化该金丝雀的无人地面值相同。 没有人的土地没有改变意味着超限不会被注意到。
所以在VS 2008中的填充值发生了变化,这样的话就会改变无人区的金丝雀,导致运行时检测到问题。
正如其他人所指出的那样,这些值的关键属性之一是指针variables,其中一个值被取消引用,这将导致访问冲突,因为在标准的32位Windowsconfiguration上,用户模式地址不会高于0x7fffffff。
关于填充值0xCCCCCCCC的一个不错的属性是在x86汇编中,操作码0xCC是int3操作码,它是软件断点中断。 所以,如果你试图在未初始化的内存中执行那些被填充的值,你会马上遇到一个断点,操作系统会让你附加一个debugging器(或者终止进程)。
它是编译器和操作系统特定的,Visual Studio将不同的内存设置为不同的值,以便在debugging器中,您可以轻松地看到是否超时进入了malloced内存,固定数组或未初始化的对象。 有人会发布的细节,而我谷歌search…
这不是操作系统 – 它是编译器。 您也可以修改这个行为 – 查看这篇文章的底部。
Microsoft Visual Studio生成(在debugging模式下)用0xCC预填充堆栈内存的二进制文件。 它还在每个堆栈帧之间插入一个空格以检测缓冲区溢出。 在这里很有用的一个非常简单的例子就是在这里(实际上Visual Studio会发现这个问题并发出警告):
... bool error; // uninitialised value if(something) { error = true; } return error;
如果Visual Studio没有预先初始化variables到一个已知的值,那么这个bug可能很难find。 使用预初始化的variables(或者说,初始化的堆栈内存),这个问题在每次运行中都是可重复的。
但是,有一个小问题。 Visual Studio使用的值为TRUE – 除0之外的任何值都是。 实际上,在Release模式下运行代码时,单元化variables可能被分配到恰好包含0的堆栈内存,这意味着您可以拥有一个单元化的variables错误,这个错误只在Release模式下显示。
这让我非常恼火,于是我编写了一个脚本 ,通过直接编辑二进制文件来修改预填充值,从而使我find了只有当堆栈包含零时才显示的未经初始化的variables问题。 该脚本只修改堆栈预填充; 我从来没有尝试堆预填充,虽然它应该是可能的。 可能涉及编辑运行时DLL,可能不会。
“为什么”的明显原因是假设你有这样的一个类:
class Foo { public: void SomeFunction() { cout << _obj->value << endl; } private: SomeObject *_obj; }
然后你实例化一个Foo
并调用SomeFunction
,它会给访问冲突尝试读取0xCDCDCDCD
。 这意味着你忘了初始化一些东西。 这就是“为什么是一部分”。 如果不是,那么指针可能与其他内存一起排列,debugging起来会更困难。 这只是让你知道你的访问违规的原因。 请注意,这种情况非常简单,但在更大的class级中,很容易犯这个错误。
AFAIK,这只适用于在debugging模式下的Visual Studio编译器(而不是发行版)
本文介绍不寻常的内存位模式和各种技术,如果您遇到这些值可以使用。
很容易看到内存已经从最初的初始值改变了,一般在debugging过程中,有时候也是为了释放代码,因为你可以在debugging器运行的时候附加debugging器。
这不仅仅是内存,许多debugging器会在进程启动时将寄存器内容设置为标记值(某些版本的AIX将一些寄存器设置为稍微幽默的0xdeadbeef
)。
这是特定于编译器使用?
实际上,它几乎总是运行时库的特征(如C运行时库)。 运行时通常与编译器强相关,但也有一些组合可以交换。
我相信在Windows上,debugging堆(HeapAlloc等)也使用特殊的填充模式,它们不同于来自debuggingC运行时库中的malloc和免费实现的填充模式。 所以它也可能是一个操作系统function,但大部分时间,它只是语言运行时库。
做malloc /新的和释放/删除相同的方式在这方面的工作?
new和delete的内存pipe理部分通常用malloc和free来实现,所以分配new和delete的内存通常具有相同的特性。
它是特定于平台吗?
细节是运行时特定的。 所使用的实际值通常被select为不仅在看hex转储时看起来不寻常和明显,而且被devise为具有可以利用处理器的特征的某些属性。 例如,经常使用奇数值,因为它们可能导致alignment错误。 使用较大的值(而不是0),因为如果循环到未初始化的计数器,会造成令人惊讶的延迟。 在x86上,0xCC是一个int 3
指令,所以如果你执行一个未初始化的内存,它会陷阱。
它会发生在其他操作系统,如Linux或VxWorks?
它主要取决于您使用的运行时库。
你能举出一些实际的例子来说明这个初始化是有用的吗?
我列出了上面的一些。 通常select这些值是为了增加发生exception情况的可能性,如果对内存的无效部分执行某些操作:长延迟,陷阱,alignment错误等。堆pipe理器有时也会为分配之间的差距使用特殊的填充值。 如果这些模式发生了变化,它就知道在某个地方有一个错误的写入(如缓冲区溢出)。
我记得读过一些东西(也许在Code Complete 2中),分配内存到一个已知的模式是很好的,某些模式会在Win32中触发中断,这将导致在debugging器中显示exception。
这是多么便携?
编写固体代码 (也可能是代码完成 )讨论了在select填充模式时要考虑的事情。 我在这里提到了其中的一些,维基百科关于魔数(编程)的文章也对它们进行了总结。 一些技巧取决于你使用的处理器的细节(比如它是否需要alignment的读写操作,以及映射到将被捕捉的指令的值)。 其他的技巧,比如使用大的值和不寻常的值,在内存转储中更加出色。
IBM XLC编译器有一个“initauto”选项,它将为自动variables分配一个您指定的值。 我用我的debugging版本以下:
-Wc,'initauto(deadbeef,word)'
如果我查看一个未初始化的variables的存储,它将被设置为0xdeadbeef