内存损坏
内存损坏通常指代码覆写一块不属于自己的内存,或者即使内存属于改写者,但错误的写操作导致内存数据超出有效范围。
内存损坏可能是我们需要调试的最棘手的问题之一。主要原因在于这类问题的发生、传播和爆发具有随机性。通常在问题发生的时刻或地方不会有什么症状,被损坏的数据要么深深潜伏在其他数据结构中,要么沿着控制流传播到很远,直到很久以后程序在看似毫不相关的地方崩溃。
段错误就是由内存损坏导致的程序崩溃是内核确定程序在访问无效内存时采取的措施
内存溢出
它发生在当用户代码访问的内存超出了内存管理器或者编译分配给用户内存块的最后1字节时。
如果用户代码的写入超过了分配内存块的用户空间,它就会覆写下一个内存块的标签。这会损坏内存管理器的堆元数据结构并导致未定义行为。只有当下一个块被释放或者分配,也就是当下一个块的标签被内存管理器用来计算的时候,破坏才会表现出来或者往下游传播。
内存管理器分配的内存块被覆写的代码示例如下:
调用strcpy之前,变量newString被分配到地址为0x501030的内存块中。标签块位于该地址的8字节之前,即0x501028,值0x21意味着这个块的大小是32字节且正在使用中。下一个块的标签可以通过将当前块地址加上其大小来计算,即0x501048,它显示了下一个块的大小是48字节,并且也在使用中(0x31)。当函数strcpy被调用以后,内存被填充了传入的字符,这个块标签没有被改变,但是下一个块的标签被字符串终止字符抹掉了。之后,当下一个内存块被用户释放时,ptmalloc将会遇到问题。
访问释放的内存
通常发生在用户代码持有指向已释放内存块的悬空指针或引用时。
释放的内存可能已经返回给内核,在这种情况下,当程序访问这个内存的时候它会立即崩溃;
释放的内存可能被再一次分配给用户,用于其他数据对象,从而导致数据对象意外地被破坏;
如果内存被内存管理器缓存,这块内存可能会被用于其内部数据结构,修改它可能会破坏堆元数据。
使用未初始化的值
一个未初始化的变量理论上具有随机和不可预测的值。根据它的使用方式,其不良影响也是不同的,可能恰巧是我们期望的缺省值(例如0),从而没有任何影响;也可能是以前变量留下的野指针,一旦使用就会导致内存损坏。
如果未初始化变量位于堆中,则其后果跟内存管理器的实现有很大关系。调试版本的内存管理发行版本跟使用不一样的分配算法是很常见的。因此,内存的分配位置以及随机性会有区别。
位于栈上的未初始化变量没有涉及内存管理器,而是通过编译器在编译时分配的。未初始化变量的内容取决于它的位置和底层内存的访问历史。因为栈随着控制流动态地扩展和收缩,栈内存不断变化。