0%

RUST-oscamp stage 3 - Hornos3

3阶段由于研究生学院和导师这边事情太多,勉勉强强完成了那个堆分配器的设计,其他很多东西都没来得及看了。

但是这个堆分配器也不是没有东西说的,正常分配不走歪门邪道的话是能够通过325个测例。

简单介绍一下这个堆分配器。这是一个参考了 musl libc 堆分配器设计思路打造的一个更轻量级的堆分配器。既然更轻量,就意味着内存空间的浪费会比 musl libc 多一些。

musl libc 与 glibc 的分配器实现思路完全不同,这个因为我是 CTF pwn 手,对这两个分配器的源代码都是有过深入研究的。musl libc 是采用大页分配后小块拆分的方式完成分配,其内部有一个数组,保存不同 chunk 的大小,在实际的内存空间中,chunk 的大小只可能是这些数组中保存的值的其中之一。对于这些不同大小的 chunk,musl libc 将相同大小的 chunk 放在一个内存页中进行分配,且一次可能会分配多个内存页,使得这些内存页能够充满 chunk。这种设计相较于 glibc 在实现上更加简单,这从 musl libc 对于堆分配器的代码实现行数就可以看得出来。因为 musl libc 强调小块内存的分配,因此每个小块的控制结构被设计地尽可能小,只要能够索引到控制结构就行。这使得一个 musl libc 中的 chunk 只需要 4 个字节的控制结构,最小的 chunk 为 16 字节,相比较下 glibc 中至少需要 32 个字节(最小的 chunk 就是 32 字节大小)。

在本人的实现中,对于数组的定义略显粗糙,使得给定的测例可能会导致较多的空间浪费,这个空间浪费最多是发生在整页的大块内存直接分配上。因为为了方便起见,大于一页的单次分配都是使用 buddy system 完成分配的,如果一次需要 0x1001 的空间,实际就会分配出去 0x2000 的空间,会造成很大的浪费,偏偏测例里面还有很多这样的内存分配,就使得最终的结果没有那么好看。

另外需要指明,本人实现的堆分配器存在天然漏洞,根本原因是没有记录 chunk 的首地址。如果一次性分配了连续的 2 页内存,再尝试从中间进行释放,是可以完成释放的。主要是因为提供的 free 接口中给出了释放的 chunk 大小,如果有办法能够欺骗外部结构,让其能够从中间释放,那么堆分配器实际上会出现问题,可能会导致 chunk 的重叠。