什么是内存腐败

当堆内存位置的内容由于编程行为而被修改,超出了原始程序构造的意图时,计算机程序就会发生内存腐败,也可以叫内存破坏;这被称为违反内存安全。内存腐败的最可能原因是编程错误。当腐败的内存内容稍后在该程序中使用时,它要么导致程序崩溃,要么导致奇怪的程序行为。Windows系统上近10%的应用程序崩溃是由于堆腐败造成的。像C和C++这样的现代编程语言具有显式内存管理和指针运算的强大功能。这些特性是为开发高效的应用程序和系统软件而设计的。但是,错误地使用这些功能可能会导致内存腐败错误。

在win32里,当程序损坏分配程序对堆的视图时,就会发生堆损坏。结果可能是相对良性的,并导致内存泄漏(有些内存没有返回到堆中,之后程序无法访问),或者可能是致命的,并导致内存错误,通常是在分配器内部。内存错误通常发生在分配程序中,当堆损坏后分配程序处理一个或多个空闲列表时。

内存腐败是最难处理的编程错误之一,原因有二:

  • 内存腐败的根源和表现形式可能相距甚远,很难将因果联系起来。
  • 症状出现在不寻常的情况下,使得很难持续地再现错误。

产生的原因

内存腐败错误大致可分为四类:

  • 使用未初始化内存:未初始化内存的内容被视为垃圾值。使用这些值可能会导致不可预测的程序行为。未初始化状态是一个常见的编程错误,可能会导致数小时的调试。基本上,未初始化状态是指已成功分配但尚未初始化为可供使用的状态的内存块。内存块可以从简单的本机数据类型(如整数)到复杂的数据块。
  • 使用非自有内存:通常使用指针访问和修改内存。如果此类指针是空指针、悬挂指针(指向已释放的内存)或指向当前堆栈或堆边界之外的内存位置,则它指的是程序当时未拥有的内存。使用这样的指针是一个严重的编程缺陷。访问这样的内存通常会导致操作系统异常,这些异常通常会导致程序崩溃(除非正在使用合适的内存保护软件)。
  • 使用已分配内存以外的内存(缓冲区溢出):如果在循环中使用数组,且终止条件不正确,则可能意外操作数组边界以外的内存。缓冲区溢出是计算机病毒利用的最常见的编程缺陷之一,在广泛使用的程序中会导致严重的计算机安全问题(如返回libc攻击、堆栈崩溃保护)。在某些情况下,程序也可能在缓冲区启动之前错误地访问内存。
  • 堆内存管理错误:内存泄漏和释放非堆或未分配的内存是堆内存管理错误导致的最常见错误。

在win32里,下面情况可能发生:

  • 程序试图释放内存
  • 程序在被释放后试图分配内存
  • 堆早在释放内存块之前就已损坏
  • 故障发生在随后的内存块上
  • 使用连续内存块
    当使用连续块时,写在边界之外的程序可能会损坏分配器关于它正在使用的内存块的信息,以及分配器对堆的视图。视图可以包括在使用块之前或之后的内存块,并且可以或不可以对其进行分配。在这种情况下,在不相关的分配或释放内存的尝试过程中,分配器可能会发生故障。
  • 你的程序是多线程的
    多线程执行可能导致错误发生在与实际损坏堆的线程不同的线程中,因为线程交错请求分配或释放内存。
  • 内存分配策略改变
    在特定内存分配策略中工作的程序,当分配策略以次要方式改变时,可能会中止。

在c++语言编程里,具体有如下原因:

  • 缓冲区溢出(上溢或下溢),这是最常见的原因;
  • 强制转换到一个错误的类型
  • 未初始化的指针
  • 错误使用. 和 ->
  • 错误使用& 和 .
  • delete & new 和 delete[] new[] 不配套
  • 缺少或者不完整的拷贝构造
  • 指向已回收内存的指针
  • 重复delete同一块内存
  • 多重基类,但是没有虚析构函数

表现形式

根据被破会的内存位置,程序会有不同的表现形式,常见就是程序崩溃掉。如果被破坏的是是堆/堆段/堆块等管理边界结构,那么在win32里,一般会触STATUS_HEAP_CORRUPTION

异常,此时的异常结构EXCEPTION_RECORD成员值一般如下:

EXCEPTION_RECORD:  
ExceptionAddress:异常地址
ExceptionCode: c0000374
ExceptionFlags: 00000001
NumberParameters: 1
Parameter[0]: 导致此异常的最终函数

0xC0000374就是堆内存腐败的异常代码,他定义如下

file:..../winnt.h
#define STATUS_HEAP_CORRUPTION ((DWORD )0xC0000374L)

怎么办

一是保证代码质量,二是提早发现。在win32里,由于堆腐败造成的崩溃几乎不可能在事后调试。避免这些问题的最佳方法是使用应用程序验证程序中的页面堆功能进行测试。页面堆有两种类型:“Full”和“Light”。Full是默认值;它将在检测到损坏时立即强制调试器停止。此功能必须在调试器下运行。然而,它也是最需要资源的。如果用户有时间问题并且已经在“完整”页面堆下运行了一个场景,那么将其设置为“轻”可能会解决这些问题。此外,轻页堆在进程退出之前不会崩溃。它确实提供了对分配的堆栈跟踪,但诊断所需的时间要比充分利用它的完全对应项要长得多。

结语

内存管理是一项众所周知的任务,需要软件开发人员高度的纪律性和谨慎性。忽略这些需求不仅会导致代码不稳定,还会导致代码易受攻击。利用内存损坏漏洞可使应用程序崩溃、操作数据,或者在最坏情况下使攻击者能够在运行应用程序的计算机上执行任意代码。根据易受攻击的应用程序的上下文,这可能意味着进一步的攻击步骤,如权限提升、后门安装或机密数据泄漏。
因此,大多数现代编程语言决定接管内存管理任务,并拥有包含垃圾收集器的运行时环境。不过,直接或间接使用手动内存管理有几个原因。像Java或C#这样的高级语言提供了一个与低级代码交互的二进制接口。这个特性经常被用来以高效的编程语言实现应用程序的性能关键部分,或者利用现有的其他语言编写的库。这些语言的解释器或虚拟机本身通常是用本地语言编写的,生成处理器可以理解的编译工件。手动内存管理的另一个原因是嵌入式系统和物联网设备的分布越来越广。它们促进了高效的实现,以降低功耗和最小化硬件需求。所以在使用想c/c++这样的语言时,我们一定要维护好我们的指针/内存/缓冲区。尽量避免发生问题。堆破坏是比较常见的一种错误类型,一般属于偶发性bug。由于其偶发的特性,排查起来比较困难,可能尝试改了很多地方,问题还是存在,就会比较头疼。

堆内存腐败异常(STATUS_HEAP_CORRUPTION---0xC0000374)的更多相关文章

  1. JVM内存区域异常分析

    在Java虚拟机规范描述中,除程序计数器外,其他几个运行时区域都有可能发生OutOfMemoryError异常.接下来将对各区域分别进行分析介绍,内容包括触发各区域OutOfMemoryError异常 ...

  2. 《深入理解Java虚拟机》-----第2章 Java内存区域与内存溢出异常

    2.1 概述 对于从事C.C++程序开发的开发人员来说,在内存管理领域,他们即是拥有最高权力的皇帝又是执行最基础工作的劳动人民——拥有每一个对象的“所有权”,又担负着每一个对象生命开始到终结的维护责任 ...

  3. JVM高级特性与实践(一):Java内存区域 与 内存溢出异常

    套用<围城>中的一句话,“墙外面的人想进去,墙里面的人想出来”,用此来形容Java与C++之间这堵内存动态分配和垃圾收集技术所围成的“围墙”就再合适不过了. 对于从事C.C++的开发人员而 ...

  4. OutOfMemoryError/OOM/内存溢出异常实例分析--堆内存溢出

    Java堆内存溢出 只要不断创建对象,并且保证GC Roots到对象之间有可达路径来避免垃圾回收机制清除这些对象, 那么在对象数量到达最大堆的容量限制后就会产生内存溢出异常,代码如下: import ...

  5. 堆内存泄漏移除导致tcp链接异常高

    故障现象: 1:活动前端Nginx服务器TCP连接数到1万多 2:活动后端Tomcat其中1台TCP连接数达4千,并且CPU瞬间到780%(配置8核16G),内存正常 3:重启后端Tomcat后,TC ...

  6. 如何写出让java虚拟机发生内存溢出异常OutOfMemoryError的代码

    程序小白在写代码的过程中,经常会不经意间写出发生内存溢出异常的代码.很多时候这类异常如何产生的都傻傻弄不清楚,如果能故意写出让jvm发生内存溢出的代码,有时候看来也并非一件容易的事.最近通过学习< ...

  7. JAVA内存管理之堆内存和栈内存

    我们常常做的是将Java内存区域简单的划分为两种:堆内存和栈内存.这种划分比较粗粒度,这种划分是着眼于我们最关注的.与对象内存分配密切相关的两类内存域.其中栈内存指的是虚拟机栈,堆内存指的是java堆 ...

  8. Java抛出OutOfMemoryError:Java heap space堆内存溢出错误的分析方案

    抛出堆内存溢出的错误一定要记得保留现场环境(导出堆内存信息到文件),否则如果无法进行分析,并从根本上解决问题,下次很有可能还会出现. 第一步:导出堆转储文件 我们可以使用Jdk自带的jmap工具.使用 ...

  9. JVM内存区域与内存溢出异常

    Java虚拟机在执行java程序时会把它所管理的内存会分为若干个不同的数据区域,不同的区域在内存不足时会抛出不同的异常. >>运行时数据区域的划分 (1)程序计数器程序计数器(Progra ...

随机推荐

  1. ansible debugger 模块

    在搞TF(tungstenfabric)时遇到了一些错误,TF通过ansible playbook 来部署的.通常情况下遇到错误都是通过ansibale xxxx –vvv 来详细输出一下.出错的类型 ...

  2. IEnumerable,ICollection,IList,List的使用

    做C#的都知道:一类只能有一个继承类,但可以实现多个接口.这句话就告诉我们:IEnumerable,ICollection,IList,List区别了 首先我看看 IEnumerable:   // ...

  3. 五 查询数据SELECT   一、单表查询

    一 单表查询的语法 二 关键字的执行优先级 三 简单查询 四 WHERE约束 五 分组查询:GROUP BY 六 HAVING过滤 七 查询排序:ORDER BY 八 限制查询的记录数:LIMIT 九 ...

  4. JavaScript 的内置对象和浏览器对象

    JavaScript 中对象通常包括两种类型:内置对象和浏览器对象,此外用户还可以自定义对象. 在 ECMAScript 中,更精确的定义应该分为:本地对象.内置对象和宿主对象. 对象包含两个要素: ...

  5. 正则表达式修饰符 i、g、m、s、U、x、a、D、e 等。

    正则表达式中常用的模式修正符有i.g.m.s.U.x.a.D.e 等. 它们之间可以组合搭配使用. i 不区分(ignore)大小写: 例如: /abc/i 可以匹配 abc.aBC.Abc g 全局 ...

  6. MySQL AutoIncrement--自增锁模式

    自增锁模式 在MYSQL 5.1.22版本前,自增列使用AUTO_INC Locking方式来实现,即采用一种特殊的表锁机制来保证并发插入下自增操作依然是串行操作,为提高插入效率,该锁会在插入语句完成 ...

  7. Linux 里的 2>&1 究竟是什么

    原文 我们在Linux下经常会碰到nohup command>/dev/null 2>&1 &这样形式的命令.首先我们把这条命令大概分解下: 首先就是一个nohup:表示当 ...

  8. 基于GDI显示png图像

    intro 先前基于GDI已经能够显示BITMAP图像:windows下控制台程序实现窗口显示 ,其中BMP图像是使用LoadImage()这一Win32 API函数来做的.考虑到LoadImage( ...

  9. NODE简易综合应用服务器搭建

    node搭建简易服务器 querystring和url模板学习地址 querystring&url 1. 目录结构 2. 代码结构 const http = require('http'); ...

  10. 如何自动加载scratch3.0的页面上实现自动加载原有的作品

    首先,我们在安装scratch3.0后,浏览器默认打开的是编程的页面.如下图: 那么我们希望开发一个功能,就是打开的时候默认加入某一个SB3的开发文件 1.首先,我们需要有一个.SB3的开发文件,建议 ...