内存漫步 - Trail of Bits博客
承认吧
偶尔有人做了某件事,你会想:“我也有过这个想法!“你会感到被认可——一个志同道合的人拥有相同的直觉、相同的见解,甚至得出了相同的结论。最近当我看到一篇描述如何使用Intel硬件事务内存来强制执行控制流完整性的论文时,我想起了这种感觉。
应用核算:通过制衡强制执行控制流完整性
不久前我也有同样的想法,写了一些代码,但从未发表。当我看到那篇论文时,我私下里,也许可以预见地,产生了那种消极的想法:“我先想到的。“这并不有建设性。让我们回到过去,当我被纠正了关于想法有"首创"这个概念的时候。别担心,硬件事务内存和控制流完整性稍后会出现。现在,我将告诉你关于Granary的故事,我的第一个二进制翻译器。
Granary
我自称为二进制翻译器。事实上,我的西装上还有这样的字母组合。作为硕士学位的一部分,我全身心投入了二进制翻译和插桩技术。我的同事Akshay和我在多伦多大学系统实验室与两位教授合作。我们发现了一个问题:Linux内核中的大多数错误实际上来自内核模块(扩展)。机会来了。一位前多伦多大学学生将DynamoRIO移植到Linux内核内部工作,该工具可以帮助捕获内核模块错误。
寻找实际错误的道路漫长而曲折。缓慢地发现错误不如快速发现酷,而且插桩整个内核来捕获模块错误并不快。我们的解决方案是仅插桩模块,让内核全速运行。这具有挑战性,并且与DynamoRIO的核心设计决策相冲突;因此,Granary诞生了。Granary的成名之处在于它可以选择性地仅插桩内核的部分内容,让其余内核原生运行。
第二名,还是第一个失败者?
随着Granary出现了地址监视点(Address Watchpoints)。这是一种进行细粒度内存访问插桩的酷炫技术。地址监视点使得可以插桩对内存中特定分配的访问,并检测缓冲区溢出、初始化前使用和使用后释放等错误。
地址监视点通过拦截内存分配函数(例如kmalloc)的调用并将污染跟踪ID嵌入返回指针的高15位中来工作。Granary使得可以介入使用这些指针的内存访问指令。它是全面的,因为受污染的指针像放射性染料一样传播——指针复制和算术透明地保留了任何嵌入的ID。
然而,事实证明地址监视点并不新颖(这是学术界的一个重要指标)。SoftBound+CETS在几年前就应用了类似的技术。保持积极!
再次不是首创
尽管缺乏新颖性,但地址监视点是实用的,并解决了Linux内核模块中内存访问错误的实际问题。Granary作为新颖性向前迈进,而地址监视点是展示Granary有用的一个应用。
我在2013年的HotDep上介绍了地址监视点,该会议与SOSP会议同期举行。在同一会议上,btkernel发布了,这是一个快速的Linux内核空间动态二进制翻译器。它应用了许多使Granary新颖的相同技术,但在完整论文发表上击败了我们。该死。
硬件事务内存
是时候再次感觉良好了。Trail of Bits在2015年我加入时给我买了一台很棒的笔记本电脑。它有一个Broadwell芯片,并支持最新的x86功能,如硬件事务内存。
并发税
硬件事务内存(HTM)的既定目的是实现无锁和高并发算法。例如,假设我想找到释放后使用错误。解决这个问题的一个可靠方法是使用指向图来表示程序。如果存在通过程序指向图的路径从释放到使用,则存在释放后使用错误。
扩展这种方法具有挑战性,但我的笔记本电脑有许多核心,所以我想象中的分身可以抛出一些并发性来解决问题并希望最好。考虑两个线程一起工作来更新指向图。它们将信息从一个节点传播到另一个节点,弄清楚什么指针指向哪里。如果它们都想同时更新同一个节点,那么它们需要同步,以便一个线程的更新不会破坏另一个线程的更新。
我们怎么知道什么时候真正需要同步?我们不知道!相反,我们需要保守地假设对特定节点的每次访问都需要同步,以防"那个其他线程"粗鲁地出现。但指向图是巨大的;我们不应该每次都需要处理最坏的情况。这就是HTM的用武之地。HTM让我们采取机会主义方法,线程不必费心尝试同步。相反,它们尝试在事务中进行自己的更改,如果它们同时这样做,那么它们的事务将中止,它们会退回到以旧方式做事。这是有效的,因为事务提供了失败原子性:要么事务成功并且线程的更改被提交,要么事务中止,就好像没有任何更改发生过一样。
那不是HTM的用途
万岁,我们现在是并发专家了。但这篇文章不是一开始就说了关于控制流完整性(CFI)的事情吗?并发性与那有什么关系?什么都没有!但HTM的失败原子性属性与CFI等有关。
事实证明,HTM可以应用于非正统问题。例如,Intel的HTM实现启用了一种侧信道攻击,可用于击败地址空间布局随机化。有一段时间,我也在研究类似的HTM误用,并且,惊喜,惊喜,我将其应用于CFI。
并行发现
两年前,我有一个关于使用HTM强制执行CFI的想法。我有一个小的概念验证脚本,一位同事帮助我编写了一个LLVM插桩通道,做了类似的事情。令我非常惊讶的是,来自Eurecom和UCSB的研究人员最近产生了相同想法的类似、更充实的实现。以下是要点。
假设攻击者通过ROP控制了程序计数器。在他们的攻击继续进行之前,他们需要枢转并使程序改变方向并走向邪恶之路。邪恶之路铺满了良好但非预期的指令。如果我们生活在一个没有后果的世界呢?如果我们让攻击者去他们想去的任何地方呢?
在普通情况下,那将是一个糟糕的想法。但攻击者控制是特殊的,有点像两个线程在巨大图中的同一节点上同时操作。HTM给我们的机会是做错事并被原谅。我们可以在函数返回指令之前开始一个事务,并在其预期目的地结束事务。把它想象成牛群放牧。唯一有效的目的地是那些结束事务的目的地。如果攻击者控制了,但没有结束事务,那么硬件最终将耗尽空间并中止事务,程序将掌握自己的命运。
我相信并且仍然认为这是一个很酷的想法。为什么我没有跟进?我设想的方法缺乏实用性。首先,如所述不够好。也许有更好的方法放牧牛群。例如,将它们对齐在围栏内。仅使用失败原子性保护CFI并不会改变游戏规则,反而只是给ROP链增加了一个额外要求。其次,硬件事务实际上并不那么快——它们会产生完整的内存屏障。一直执行事务会杀死ILP和并发性。这使得这种技术对于像Google Chrome这样的真实程序来说是不可能的。最后,Intel新的CFI CET指令集使这种方法一出现就失效。CET以类似的方式提供特殊指令来绑定控制流目标。
如果森林中有一棵树倒下
如果我有这个想法,而Eurecom/UCSB小组也有这个想法,那么我打赌某个未知的第三方也有这个想法。也许Martin Abadi某天梦到了这个,但没有告诉世界。也许这就像操作系统,或分布式系统,或者真的只是…系统,类似的问题和解决方案似乎每二三十年就会重新出现。
看到别人有和我一样的想法真的很令人耳目一新。它让我同时感到好和坏,并让我想起了几年前的一个有趣时光,那时我觉得自己在做一些聪明的事情。不过,我不是一片特殊的雪花。想法没有首创。Eurecom/UCSB小组有一个想法,然后他们跟进,产生了一个实现,并对其进行了评估。这才是重要的。
如果你喜欢这篇文章,请分享: Twitter LinkedIn GitHub Mastodon Hacker News
页面内容
Granary 第二名,还是第一个失败者? 再次不是首创 硬件事务内存 并发税 那不是HTM的用途 并行发现 如果森林中有一棵树倒下
近期文章
构建安全消息传递很难:对Bitchat安全辩论的细致看法 使用Deptective调查你的依赖关系 系好安全带,Buttercup,AIxCC的评分回合正在进行中! 使你的智能合约超越私钥风险 Go解析器中意想不到的安全隐患
© 2025 Trail of Bits. 使用Hugo和Mainroad主题生成。