基于紧凑内存池的内存访问异常监控方法

文档序号:31598792发布日期:2022-09-21 07:58阅读:62来源:国知局
基于紧凑内存池的内存访问异常监控方法

1.本发明属于计算机技术领域,具体涉及一种基于紧凑内存池的内存访问异常监控方法。


背景技术:

2.c/c++的优点使其具有极大的吸引力,并且在某些情况下必不可少:它运行速度优异、内存和磁盘空间小、成熟、执行可预测,并且它们的平台适用性几乎无与伦比,在一个平台上无需安装任何其他工具即可使用这门语言。但与java、golang这类运行时系统不同,c/c++语言为了出色的程序运行效率,并没有运行时系统提供的内存管理机制,需要用户手动的管理内存。这就导致c/c++程序出现了其他语言(带内存管理机制的语言)不同的漏洞,即内存安全问题。
3.针对c/c++程序内存安全问题,开源社区先后涌现出一大批内存安全检测工具,如:valgrind,addresssanitizer,hwaddresssanitizer等。asan(addresssanitizer)在2012年由google开源,由于其优异的性能和额外资源消耗更低而普遍应用于程序检测中,逐渐替代了valgrind。asan通过源码插桩和动态链接库的方式,对程序的指针越界,释放后使用等内存安全问题进行捕获。主要是通过shadow memory(影子映射内存),redzone(内存保护区)、memory quarantine(内存隔离区)及其他辅助数据结构完成。
4.由于分配给应用程序的所有内存都是可以正常访问的,并没有一个机制能够有效区分指针越界或释放后使用,因此,通过引入shadow memory来标识内存是否可以访问,每一个应用程序使用的内存都有一块shadow memory与之对应,当程序可以访问这块内存时,置shadow memory为自然数,当该内存不可访问时,则置为负数。如图1所示,为shadow memory的示意图。
5.shadow memory的引入,将程序的内存分门别类。接下来,就是内存安全问题归结为非法内存,使shadow memory可以有效区分。由于应用程序要尽可能充分的使用内存,所以两个变量之间是紧密排列的,没有间隙。asan通过引入内存保护区用于捕获指针越界问题,当需要分配一个内存块时,在内存块的两端多分配一些内存,大小根据想要分配的内存按比例计算,称之为内存保护区。把内存保护区的shadow memory置为不可访问,这样当发生指针越界时,会访问到内存保护区,即可捕获到指针越界。
6.程序的变量分为全局变量,局部变量和堆变量。本发明主要围绕堆变量的内存分配问题。程序需要维护内存池来高效合理的分配堆变量,但由于堆变量的特殊性,内存安全检测工具使用了类似于slab机制的内存池,但这种方式比较消耗内存。
7.现有类slab内存池机制,为16b、32b、

、65536b这30种内存块,分别分配一个固定大小,如1mb大小的内存池,专门用来分配固定大小的内存块,当用完,则再分配一个1mb的内存池。如图2所示,就是两个1mb的内存池,分别用来分配16b和32b的内存块。这个内存池可以满足一个内存块的任何地址都可以定位到首地址,每个内存池都是1mb地址对齐的,k字节内存块在内存池中是k字节对齐的。由于操作系统分配物理内存的最小单位是一个物
理页,一般为4kb,而在使用类slab机制分配一块内存池,如1mb内存池来分配特定大小的内存块chunk,如16b,如果程序只分配很少次16b,如分配5次,则4kb-5*16b的内存都被浪费掉。不仅仅如此,内存块是离散的也就导致了shadow memory是离散的,也导致了shadow memory内存消耗的增长。
8.现有类slab内存池机制,由于内存保护区的存在,内存块的首地址(chunk_begin)与应用程序获得的内存首地址(user_begin)不同,所以,该内存池可以将应用程序获得的首地址user_begin转换为内存块的首地址chunk_begin。当访问到一个非法地址时,可以根据这个地址获得其内存块的相应信息,所以该内存池应满足任何地址都可定位到该内存块(而不仅仅首地址)。如图3所示,为类slab内存池机制获得内存首地址的伪代码,图中,chunk_size表示内存块大小,pool_begin表示分配的内存池的起始地址。


技术实现要素:

9.针对上述堆变量的内存分配问题,使用类似slab机制的内存池分配内存,当分配特定大小的内存块较小且分配次数少时,存在内存消耗大的问题,本发明提供了一种基于紧凑内存池的内存访问异常监控方法,以解决asan中堆变量分配内存中存在的这种问题。
10.本发明提供的一种基于紧凑内存池的内存访问异常监控方法,对内存访问错误检测工具asan的内存池进行优化,改进堆变量的内存分配方式。本发明方法包括:
11.(1)对同一应用程序的堆变量,将不同大小的内存块用同一个内存池来分配存储地址;
12.(2)将分配的每个内存块对应红黑树的一个节点,利用内存块后端的内存保护区维护一颗红黑树;在内存块的后端内存保护区内存储节点信息;节点信息包括内存块的起始地址,以及该节点的左右两节点。
13.红黑树中节点所代表的内存块的地址范围为[内存块的起始地址,后端内存保护区的地址]。
[0014]
(3)当需要释放内存时,根据要释放的内存首地址user_begin在红黑树中检索,寻找对应的节点,判断内存首地址是否在节点对应的地址范围内,若在,表示要释放的内存属于该红黑树对应的内存池,将该节点从红黑树中删除,并归还内存;若不在,表示要释放的内存不属于该红黑树对应的内存池,此时反馈程序出现bug。
[0015]
本发明的优点与积极效果在于:
[0016]
(1)使用本发明方法,将所有的内存块采用一个内存池进行分配,并非需要为每种大小的内存块都申请一个特有的内存池进行分配,避免了一个用来分配固定大小为k的内存池只分配一次,之后再没有进行分配的情况,减少了内存使用浪费。
[0017]
(2)现有方案由于存在多个内存池,会导致多个碎片化的shadow memory,而本发明方法只存在一个内存池,对应的shadow memory也集中在一块,自然shadow memory消耗的内存更少。
[0018]
(3)本发明方法通过在内存块后端的内存保护区维护一颗红黑树,一方面实现了可以快速检索到内存块的首地址,另一方面通过红黑树节点的地址范围可以发现程序异常。
附图说明
[0019]
图1是影子映射内存的示意图;
[0020]
图2是现有类slab内存池的示意图;
[0021]
图3是现有类slab内存池机制获得内存首地址算法的示意图;
[0022]
图4是本发明方法采用的紧凑内存池的示例图;
[0023]
图5是本发明方法利用内存保护区维护红黑树的示意图;
[0024]
图6是本发明的分配内存机制与现有类slab内存分配机制的使用对比图;
[0025]
图7是本发明方法与现有类slab方法在释放内存机制的对比图。
具体实施方式
[0026]
下面将结合附图和实施例对本发明作进一步的详细说明。
[0027]
本发明提出一种基于紧凑内存池的内存访问异常监控方法,采用了紧凑内存池的分配方案,即为同一应用程序堆变量的不同大小的内存块均采用一个内存池来进行分配,如图4所示,但采用这种内存池失去了类slab机制内存池得天独厚的通过常数即可取到内存块首地址的优势。那么,就需要解决这种内存池如何获取首地址的问题。由于每次分配内存块时都会在两端额外分配内存保护区以捕获指针越界,在前端的内存保护区会保存内存块的一些元信息,比如:分配栈,释放栈,内存块大小等等。而在后端的内存保护区是没有被充分利用起来的。所以,本发明充分利用后端的内存保护区来维护一个高效的索引结构,采用红黑树解决首地址获取问题。
[0028]
如图5所示,本发明使用后端的内存保护区维护了一棵红黑树。当要分配一个内存块时,将对应生成红黑树中的一个节点。每个内存块在后端的内存保护区中存储红黑树的节点信息。节点信息包括内存块的起始地址,而结束地址则是后端内存保护区的地址。这样,这个红黑树就可以满足两个功能:
[0029]
(1)判断一个内存是否是该内存池分配;
[0030]
(2)确定该内存块的起始地址chunk_begin。
[0031]
本发明实施例中,红黑树节点信息的数据结构定义如下:
[0032][0033]
其中,node为指针型数据,left和right分别代表了左右子树节点,addr表示该内存块的起始地址chunk_begin。红黑树节点object所代表的地址范围是[addr,&object],&object表示取节点object对应的后端内存保护区的地址。
[0034]
将上面这样的数据结构存储在内存块后端的内存保护区中,从而可实现对内存块地址的高效检索。
[0035]
如图6所示,其中,

是类slab分配内存机制,从相应大小的内存池中拿一个内存块,返回给应用程序。

是本发明的紧凑内存池机制,先是在大内存池中拿一块大小合适的
内存块,而后,根据后端的内存保护区记载的内容将内存块作为一个节点插入到红黑树中,最后返回给应用程序。
[0036]
例如,当为一个k大小的内存分配内存块时,首先将k对齐到2nb大小,根据内存块大小来计算左右两端的内存保护区大小,然后在后端内存保护区中记录内存块起始地址,记录节点插入红黑树后的左右子树节点信息。
[0037]
如图7所示,其中,

是类slab释放内存机制,由于内存池的特殊性,即每个内存池只分配一种大小的内存块,所以可以根据块大小确定内存块的首地址。图7中

是本发明的紧凑内存池释放内存机制,首先去红黑树中检索这个地址所在的内存块,然后根据节点记录的内存块的首地址,这就是想确定的首地址,而后调整红黑树,归还内存。
[0038]
本发明实施例中,释放内存时,实际上就是根据内存首地址进行释放,如下:
[0039]
分配内存时,void*ptr=malloc(sizeof(int));
[0040]
释放内存时,free(ptr);//用申请内存返回的指针进行释放。
[0041]
其中,malloc为申请内存空间,ptr表示要释放的内存首地址。
[0042]
若要释放的内存块是从堆上申请的内存块,就会在红黑树的一个节点上,根据从红黑树检索到的节点的起始地址addr,以及该节点对应的后端内存保护区的地址&object,判断要释放的内存块释放是从该内存池进行分配的。若满足ptr≥addr&&ptr《&object,证明这个内存块是从该内存池进行分配的,并且这个内存块的首地址就是addr。然后将这个节点从红黑树中删除,此时可能红黑树不平衡,按照正常的红黑树算法对红黑树的节点进行调整。若程序出现bug,释放了一个不属于该内存池的内存块,如释放了一个全局变量,此时ptr不在节点对应的地址范围内,通过在内存释放前首先判断,所释放的内存是该内存池分配的,可以捕获这种程序故障。
[0043]
通过上面实现过程,本发明针对内存访问错误检测工具asan的内存池进行了优化。asan通过增加内存保护区来捕获指针越界,内存保护区是指在变量前后各加一定大小的内存,这样当出现指针越界时就访问到了内存保护区,进而捕获到指针越界。但有了内存保护区,就出现了user_begin!=chunk_begin的情况,因此原有的内存池采用类slab内存池,这个内存池可以实现user_begin常数时间转换成chunk_begin,但所带来的负面影响是消耗内存增大。所以本发明方法提出使用紧凑内存池代替类slab内存池,并且提出用右侧内存保护区建立红黑树以实现user_begin以o(logn)的时间复杂度找到chunk_begin。并且经实际测试发现,asan采用本发明方法进行内存访问错误检测的性能并未有下降。因此,使用本发明方法减少了碎片化的shadow memory的情况,减少了内存的浪费。
[0044]
除说明书所述的技术特征外,均为本专业技术人员的已知技术。本发明省略了对公知组件和公知技术的描述,以避免赘述和不必要地限制本发明。上述实施例中所描述的实施方式也并不代表与本技术相一致的所有实施方式,在本发明技术方案的基础上,本领域技术人员不需要付出创造性的劳动即可做出的各种修改或变形仍在本发明的保护范围内。
当前第1页1 2 
网友询问留言 已有0条留言
  • 还没有人留言评论。精彩留言会获得点赞!
1