LWN:让kmalloc() 更随机!

关注了就能看到更多这么棒的文章哦~

Randomness for kmalloc()

By Jonathan Corbet
July 24, 2023
ChatGPT assisted translation
https://lwn.net/Articles/938637/

内核的地址空间布局随机化(ASLR, address-space layout randomization)希望通过在每次启动时更改内核的 text 和 data 的放置位置,使攻击者难以进行攻击。希望通过这种随机化使得攻击者无法提前确认在某一个特定系统上希望利用的易受攻击目标地址。然而,有一些技术能够在不用精确知道特定对象的存储位置的情况下就产生效果。为了增强系统对此类攻击的防御,内核会引入另一种形式的随机化。

"Heap spraying" 希望能在目标系统的堆(heap)里用已知数据来进行填充;通常是通过分配大量内存并将其填充为感兴趣的数据来实现的。成功攻击的情况下可以将 heap 的大部分内容填充为已知的数据样式。如果可以让目标系统来对 heap 中的无效指针进行解析引用(dereference),那么很有可能会访问到攻击者控制的数据。

举个例子,来考虑一下 use-after-free 的漏洞。在对象被释放之后,攻击者就可以重新分配并覆盖这个对象。然后只需要等待目标代码使用(这个现在已经无效了的)指针,那么就搞定了;本来让攻击者可以分配和写入一些内存只是一个简单的漏洞,而这下就升级为更严重的问题了。不用说,攻击者会非常喜欢这种功能;heap spraying 已经用在了许多成功的攻击中。

在内核中,heap 是一个有些模糊的概念;你可以将其视为包括了系统中几乎所有物理内存。但是内核中的大部分内存管理是由 slab 分配器处理的。理论上,Linux 内核中有数百个独立的 slab;/proc/slabinfo 在编者的系统上显示有近 300 项。这个数字反映了内核中许多地方都在使用 slab,因为各个子系统需要分配不同大小的对象。这种隔离会有助于阻止 heap-spraying 攻击,因为这会限制对一个 slab 进行 spray 攻击的时候不会影响其他 slab。攻击者无法对不能从中分配内存的 heap 来进行 spray 攻击。

然而,在实践中,用户之间的隔离并没有看上去那么完好。kmalloc()调用(内核中大部分内存的分配方式)共享了一组共同的 slab。在内核中有数以万计的 kmalloc()调用(以及类似 kzalloc()的变体),因此以这种方式获得的内存是 heap-spraying 攻击的一个相当明显的目标。slab 分配器还经常会把包含了相似大小对象的 slab 和病起来,再次将多个目的的分配放入了同一"heap"中。总之,Linux 内核也可能受到 heap-spraying 攻击的影响。

正如 Ruigi Gong 在其 patch 中指出的,为了防止 spraying 攻击,将这些 slab 用户分开并不是一个足够的方案。所有这些 kmalloc() 的调用位置都不太可能会进行更改,而完全关闭 slab 的合并功能也会有性能损失。相反,理论上,如果可以在某种程度上将所有这些 kmalloc() 用户相互分开,可能会获得更好的保护。

kmalloc()分配器使用了一组约十几个 slab 来为不同大小的对象进行分配。在编者的系统上,最小的 slab 用于八字节的对象,而最大的 slab 用于 8KB 的对象,从一个 slab 到下一个 slab 通常会 size 翻倍。当调用 kmalloc()时,所请求的 size 将向上对齐到最近的 slab size,并从该 slab 进行分配。因此比如说一个 36 字节的请求将导致分配 64 字节的内存。让事情变得更加复杂的是,实际上有四组 slab,如下所述:一组用于"normal"分配,一组用于分配 DMA 使用的内存,一组用于被标记为 reclaimable 的内存(使用__GFP_RECLAIMABLE 分配的),以及一组用于 non-reclaimable 的内存,由 cgroup 来管理。

Gong 的 patch set 通过为每个 size 都添加另外 15 个 slab 给这个矩阵添加了一个新的维度,但仅适用于"normal"分配。每当 kmalloc()调用属于正常类别(大部分情况下是这样),就随机选择适当大小的 16 个 slab 之一,并从该 slab 进行分配。通过这种方式,希望攻击者可以 spray 的任何内存都将与其期望攻击的那种易受攻击代码所使用的内存隔离开。

为了能更好地按这个方向发展,已经对分配随机 slab 的选择动作进行了一些思考。在这个计算中使用了两个值:在系统启动时生成的随机数种子,以及 kmalloc()被调用的地址。因此,任何一个特定的 kmalloc()调用点都总是会从相同的 slab 分配,但具体是哪个 slab 将在每次启动后发生变化。因此,对于攻击者来说,不仅仅是需要进行更多的分配来 spray 污染所有相关 slab,而是必须找到一个在当前这次系统启动之后正确的 slab 的调用。这并不是达到绝对安全的防御措施,但以这种方式分割 slab 会大大提高攻击得手的难度。

patch 附带的基准测试显示,在启用此功能时性能开销很小,内存使用略有增加;这种成本似乎足够低,大多数用户可能都不会注意到。作为对之前帖子的回应,Kees Cook 曾评论说,它在对抗 heap-spraying 攻击方面提供了"不错的平衡"。第五版修订版于 7 月 14 日发布,并迅速由 slab 维护者 Vlastimil Babka 合入了。这项工作现在已经进入了 linux-next,并有望在 6.6 合并窗口期间进入 mainline 内核。

全文完
LWN 文章遵循 CC BY-SA 4.0 许可协议。

欢迎分享、转载及基于现有协议再创作~

长按下面二维码关注,关注 LWN 深度文章以及开源社区的各种新近言论~

format,png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值