LWN:6.4里也许会支持用户空间影子堆栈了!

软件作为信息系统的关键组成和代码重用攻击的防护 软件作为信息系统的关键组成, 控制着计算机系统设备的工作方式, 其安全性关系到整个信息系统的安全、可靠和稳定.而软件作为人类智力活动的表达和产物, 在其规模和复杂程度迅速增长的同时, 就不可避免地存在漏洞(和缺陷)[1-3].在当前严峻的网络空间(cyberspace)安全形势下, 软件漏洞以其高威胁、难防御、普遍存在等特点, 被作为一种战略资源, 广泛用于攻防博弈中[4-6].而本文则是在当前系统架构和软件生态环境下, 从攻击和防护两个角度对软件漏洞利用中的二进制代码重用关键技术展开探讨和研究. 自19 阅读详情

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

User-space shadow stacks (maybe) for 6.4

By Jonathan Corbet
March 24, 2023
DeepL assisted translation
https://lwn.net/Articles/926649/

x86 架构的影子堆栈(shadow stacks)已经支持很久了;LWN 在 2018 年首次报道了这项工作。不过经过五年的时间和众多的版本演进之后,似乎 x86 上的用户空间影子堆栈(user-space shadow stack)可能只能在 6.4 内核版本中才得到支持。自从我们上次在 2022 年初赶上这项工作以来,它已经经过了不少变化。

影子堆栈是防御面向返回编程(ROP, return-oriented programming)攻击的一种方式,也能防止其他一些针对进程调用栈(call stack)的攻击。影子堆栈本质上就是硬件维护的在每个函数的调用被放到 call stack 里的时候对返回地址所做的一个副本记录。任何破坏调用栈的攻击都无法改变影子堆栈中的相应项目;因此,在函数返回时就会发现出现了问题,并在攻击者能够控制之前就把该进程终止。2022 年的 LWN 文章中有更多关于 x86 影子堆栈工作方式的细节信息。

当前版本的 patch set 是 Rick Edgecombe 发布的第八个修订版(他在 Yu-cheng Yu 发布了大约 30 个修订版后接手了这组 patch)。

API changes

用于处理影子堆栈的用户空间 API 在过去一年中没有什么变化。大多数操作都是通过 arch_prctl()调用发起的,具体来说是:

  • ARCH_SHSTK_ENABLE 为当前线程打开影子堆栈;影子堆栈在进程启动时不会被内核缺省打开。

  • ARCH_SHSTK_DISABLE 让当前线程停止使用影子堆栈。

  • ARCH_SHSTK_LOCK 阻止对线程的影子堆栈状态进行后续更改。这个操作的用途之一是可以阻止攻击者在破坏调用栈之前以某种方式禁用影子堆栈。

  • ARCH_SHSTK_UNLOCK 可以撤销 ARCH_SHSTK_LOCK 的效果。这个选项在 12 月被添加到第四版 patch set 中;它之所以存在,是为了支持像用户空间的 Checkpoint/Restore 这样的功能,需要在进程启动后能够改变影子堆栈的状态。这个选项只可以通过 ptrace() 调用;进程不能直接对自己发起这个调用。

  • ARCH_SHSTK_STATUS 返回当前的影子堆栈状态。

通常情况下内核会处理影子堆栈的分配和存放位置,但在某些情况下,应用程序需要自己直接管理其影子堆栈。map_shadow_stack()系统调用就是为了这个目的而创造的;它的原型在过去的一年中发生了一些改变:

void *map_shadow_stack(unsigned long address, unsigned long size,
         unsigned int flags);

这个调用将尝试在指定地址上建立一个指定 size 的影子堆栈,成功后返回实际映射的地址。flag 的一个可能的值是 X86_FEATURE_USER_SHSTK;它要求必须提供 "restore token" (用途之一是防止多个线程共享同一个影子堆栈) 被存储到新创建的堆栈中。

还是以前那个 SHSTK
有一次,Andrew Morton 抱怨 "shstk" 这个缩写,说它 "听起来像我在醉酒时用俄语骂人"。结果,这个术语被从大部分通用代码中抽出,但在 x86 部分仍然存在。

map_shadow_stack()还有一个微妙的改动,会影响到影子堆栈的通常处理方式。影子堆栈功能跟 32 位代码不兼容,具体来说是在牵涉到 signal 时。内核会拒绝为运行在 32 位模式下的线程启用影子堆栈,在第四版 patch set 中,如果一个进程在影子堆栈启用后切换到 32 位模式,就用代码来直接禁用所有的 signal handler。

除了看起来有点像 hack 的做法外,它也没有完全解决这个问题。事实证明,一个 64 位线程可以在内核不知道或不允许的情况下切换到 32 位模式,这意味着信号处理程序的禁用可以被规避掉。在人们讨论了一段时间如何避免发生这种情况之后,大家决定(在第 5 版中)改成总是将影子堆栈映射到 4GB 以上的虚拟地址,使其无法被 32 位代码访问到。因此,当影子堆栈被启用时,任何试图切换到 32 位模式的做法都会导致立即 crash。

这个改动引入了一个新的 mmap()标志 MAP_ABOVE4G,它会强迫在 4GB 的虚拟地址边界之外来创建 mapping 映射。传递给 map_shadow_stack() 的地址(如果是 0 的话就表示不指定特定地址)也必须在 4GB 以上,否则调用会失败。今后如果有一天某位开发者也许有动力的话会找到一种方法来使 32 位代码也能配合影子堆栈一起工作,但是考虑到人们对 32 位代码的兴趣非常小,这似乎不太可能发生了。

The glibc problem

尽管在运行所有程序时都启用影子堆栈可能是件好事,但有些应用程序在这种环境下会被破坏掉。任何会修改自己的调用栈的程序(例如 just-in-time compiler)都会发现自己修改后会跟影子堆栈不再同步,从而导致异常终止。因此,影子堆栈要想启用就必须限制在能够处理它的代码上。

前段时间开发的方案是在程序的可执行镜像的 .note.gnu.property ELF 部分放置一个特殊的 note 注释。如果这个注释存在的话(编译器在构建程序时有选项可以指定),这表明可以安全地启用影子堆栈来运行程序。但这个 note 并不足以让内核做出决定,所以启用影子堆栈的工作就留给了用户空间,具体来说就是 C 库的程序加载器(program loader)。

在 GNU C 库(glibc)的社区中,热情的开发者很快就为开启影子堆栈提供了支持,只等它准备好了;当前版本的 glibc 已经准备好在内核支持影子堆栈的时候打开这个功能。只有一个小问题:glibc 的支持是根据早期版本的用户空间 API 来编写的。那个 API 已经不存在了;试图使用它的话,会导致程序 crash 并且无法运行。这的确可以保证它不受 ROP 攻击,但用户可能会对采用这种方式保证安全的做法会很愤怒,可能会找我们抱怨。

这个问题之前通过改变 API 来解决了,这样 glibc 根本找不到这个 API,于是就认为影子堆栈的功能不存在了。不过,glibc 开发者已经说过,他们打算在新的影子堆栈 API 被合并到内核之后就马上用起来。这样一来当新版 glibc 在系统上运行起来时,任何表示准备使用影子堆栈的程序都会可以真的用起来。

这就导致了一个新的问题,正如在第三版的邮件说明中所指出的:并不是所有被标记为准备好的应用程序都是真的准备好了:

但是今天存在许多应用程序二进制文件都带上了这个 bit,关键是,它在一些流行的发行版编译的时候就直接对大批量的应用程序直接打开了,而根本没有验证这些软件包是否真的支持影子堆栈。因此,在 glibc 更新时,影子堆栈将突然会被大量应用程序打开,而缺少测试验证。

在这种环境下会崩溃的应用程序就包括了 node.js 和 PyPy,所以这似乎是一个真正的麻烦。在 Fedora 37 系统上的快速检查显示,PyPy 确实是在启用影子堆栈的情况下编译出来的:

$ readelf -n /usr/bin/pypy
Displaying notes found in: .note.gnu.property
  Owner                Data size        Description
  GNU                  0x00000040       NT_GNU_PROPERTY_TYPE_0
      Properties: x86 feature: IBT, SHSTK
[...]

即使根本原因在于用户空间,那它也可以通过升级到新的内核来触发问题,因此看起来就好像是内核的改动导致了问题一样(regression)。内核开发者通常喜欢避免破坏大家的系统,哪怕可以说是由于别人的错误。

Edgecombe 认为,最理想的解决方案是直接换一个新的 ELF bit 来识别真正的影子堆栈的准备情况,并让 glibc 使用它来判断。然后可以鼓励发行商更小心地将应用程序标记为可以使用影子堆栈。但是他说,"看起来 glibc 的开发者对解决这个问题并不感兴趣",所以还需要其他一些改动。在第 3 版中就提供了这样一个 patch,当检测到 ELF bit 时禁用影子堆栈 API。我们的想法是,一旦发行商确认他们运送的所有软件包中的二进制文件都是正确标记过的,那么他们就会禁用该检查。

这个 patch 被人们称为 "a bit dirty",并且是为了搜集人们的反馈意见而加入的,结果确实达到了目的。H.J. Lu 建议,正确的做法就是避免升级 glibc,直到系统准备好为止。Florian Weimer 补充说,大多数不兼容的代码都是在进程启动后加载的库文件中;内核测试不会发现这些,而且要再禁用影子堆栈的话可能就太晚了。

过了一段时间,Edgecombe 问 Linus Torvalds 他认为应该如何处理这个问题。Torvalds 回答说,他不想在没有理由的情况下预先禁用影子堆栈的支持:

一旦[影子堆栈功能]在内核中被启用,并且人们抱怨它破坏了现有的二进制文件,在那个时候,我想它会被再次禁用。那时可能会使用类似你建议的这个 patch。但在实际问题出现之前,以及在我们真正地在内核中使用这段代码之前,我不会这么做。

禁用影子堆栈 API 的 patch 已经从这个系列中删除了。Weimer 描述了几个方案,以确保影子堆栈可以在发行版中安全地启用,并声称采用新的 ELF bit 会让这个过程大大延迟。他说,支持影子堆栈与支持一个新的系统调用没有什么不同;新增系统调用也会破坏现有的应用程序的,主要是因为 seccomp() 过滤器不理解新的调用而引起的。

On to 6.4

讨论的结果是,内核不会采取特别的措施来避免破坏那些被错误地标记为准备使用影子堆栈的二进制文件。至少在出现问题之前不会这么做。大多数其他悬而未决的问题似乎都得到了解决,因此 Edgecombe 在当前版本的封面信中就说:"我们当前有了一个相当好的初始版本的影子堆栈实现了"。他说,还有一些需要改进的地方,但在对现在的代码进行了一些实际使用之后,这些改进可能可以做得更好。

因此,在所有这些工作之后,40 个影子堆栈 patch 已经被添加到了 tip tree 中,它会把这些 patch 引入到 linux-next 中。如果在接下来的一个月左右的时间里没有出现令人震惊的问题的话,x86 系统的用户空间影子堆栈支持很可能会在 6.4 合并窗口中走到 upstream 里。经过漫长的开发期,影子(堆栈)最终将真正知道 ROP 攻击者的心里都在打着什么算盘。

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

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

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

format,png

pin-shadow-stack:像 ROP Defender 影子堆栈 DynamoRIO 版本 - (用于安全的运行时二进制检测) 这实现了一个影子堆栈,它保留每个返回地址的副本以防止 ROP 攻击。 它还处理setjmp / longjmp 、unix 信号和 C++ 异常处理 (Itanium ABI)。 如何使用 将放入../pin-2.14 make (静默)或make debug (非常详细) make run使用 hello world 立即下载

相关推荐

ASTM D4583-21(2025).pdf

ASTM D4583-21(2025)

Intel CET缓解措施深度研究

这是一个争议比较大的问题,有的人会建议先学编程,而有的人会建议先学计算机基础,其实这都是要学的。第一种是报网络安全专业,现在叫网络空间安全专业,主要专业课程:程序设计、计算机组成原理原理、数据结构、操作系统原理、数据库系统、 计算机网络、人工智能、自然语言处理、社会计算、网络安全法律法规、网络安全、内容安全、数字取证、机器学习,多媒体技术,信息检索、舆情分析等。此外,makecontext会创建⼀个新的数据栈,开辟⼀个新的上下⽂,和上⾯的场景⼜有些许不同,makecontext和。

dzqxwzoe的博客 299

LWN用户空间影子堆栈

关注了就能看到更多这么棒的文章哦~Shadow stacks for user spaceBy Jonathan CorbetFebruary 21, 2022DeepL assisted...

Linux News搬运工 2217

Shadow Stack(CFI)(控制流完整性技术)

摘要:ShadowStack(影子栈)是一种防御ROP攻击的关键控制流完整性技术,通过创建独立的受保护栈存储返回地址。其采用双栈结构(常规栈和影子栈),在函数调用/返回时进行地址比对,不匹配则触发异常。实现方式包括硬件支持(如Intel CET、Arm GCS)和软件方案,性能损耗低。优势包括高效防御ROP、低开销和与其他安全技术互补,但面临兼容性、高级绕过等挑战。广泛应用于操作系统内核、浏览器引擎等领域,未来将结合硬件扩展和混合CFI方案持续演进。该技术显著提升漏洞利用门槛,成为高安全场景的标配机制。

fearhacker的专栏 1581

保护函数返回的利器——Linux Shadow Call Stack

从而避免了传统的、针对栈上返回地址的攻击方法。在通常的函数调用中,被调用函数的返回地址存储在栈上,攻击者可以通过篡改栈上返回地址劫持程序的执行流,常见的攻击方式如通过溢出覆盖返回地址、ROP(Return Oriented Programming)攻击等。在OPPO FIND X3项目上,使用IDA逆向bootimage,检测任意内核地址的返回值,确认具备SCS的明显特征,如下截图。将 X18 寄存器中存储的地址值减去 8 字节,然后将存储在该地址处的数据加载到 X30 寄存器中,同时更新 X18 的值。

youzhangjing_的博客 415

LWN:介绍 PyScript!

关注了就能看到更多这么棒的文章哦~Introducing PyScriptBy Jake EdgeJune 22, 2022PyConDeepL assisted translationhttps://lwn.net/Articles/898452/在犹他州盐湖城举行的 PyCon 2022 的主题演讲中,Peter Wang 介绍了浏览器内 Python 解释器领域的另...

Linux News搬运工 1013

6.6内核版本主要特性

2023年10月29日星期天,Linux 6.6发布。

weixin_49382066的博客 1303

LWN:5.18合并窗口第一部分!

关注了就能看到更多这么棒的文章哦~5.18 Merge window, part 1By Jonathan CorbetMarch 25, 2022DeepL assisted translationhttps://lwn.net/Articles/888736/截至目前,在 5.18 开发周期中已有 4127 个 non-merging changeset 进入了 ma...

Linux News搬运工 498

LWN:用 clone3() 控制影子堆栈

影子栈 (shadow stack) 是一种控制流完整性 (control-flow-integrity) 功能,旨在防御通过操纵线程的调用栈 (call stack) 来进行的攻击。虽然调用栈上会压入相当多的信息——例如局部变量 (local variables) 和保存的寄存器 (saved registers)——但影子栈只保存返回地址,因此相同大小的影子栈很可能对线程来说过于庞大。每当一个新的线程产生时,内核都会根据其常规栈的大小,为其分配一个相同大小的影子栈,作为对正确大小的最佳猜测。

Linux News搬运工 1479

《intel开发手册卷1》学习笔记2

向量 32 至 255 为软件定义中断,可用于软件中断或可屏蔽硬件中断。请注意,处理器还定义了几个不指向 IDT 中条目的额外中断;其中最著名的是 SMI 中断。异常被分类为故障、陷阱或中止,具体取决于异常的报告方式以及导致异常的指令是否可以在不丢失程序或任务连续性的情况下重新启动。1)故障— 故障是一种通常可以纠正的异常,并且一旦纠正,就可以重新启动程序而不会失去连续性。当报告错误时,处理器将机器状态恢复到开始执行错误指令之前的状态。

qq_15054345的博客 1008

Shadow Call Stack and Branch Target Identification for improved security on ARM64

Shadow Call Stack and Branch Target Identification for improved security on ARM64

小立仔 1691

影子堆栈(Shadow Stack)

摘要:影子堆栈(ShadowStack)是一种高效的控制流完整性防护技术,通过创建独立受保护的返回地址副本栈,阻断ROP/栈溢出攻击。其核心原理是数据分离和双写双读机制,软件实现(如ARM64的SCS)通过编译器插桩实现低开销防护,硬件实现(如x86的CET)则提供更强安全性。该技术兼容性好,性能损失低(<1%),已成为ARM64和x86平台的主流防护方案,能有效防御返回地址篡改攻击,但需配合其他技术防护间接调用漏洞。

tangzhangyin的专栏 762

LWN:内核硬件CFI的支持

关注了就能看到更多这么棒的文章哦~Kernel support for hardware-based control-flow integrityJuly 11, 2022This article was contributed by Mike RapoportDeepL assisted translationhttps://lwn.net/Articles/90009...

Linux News搬运工 1124

ShadowStackView

ShadowStackView 项目地址:REBOOTERS/ShadowStackView 简介:Create something like Shadow-View animation when drag the view on screen 更多:作者提 Bug 标签: Create something likeShadow-Viewanimation whe...

未来很长,但我会努力的走下去。 534

what is the StackShadowPages

It seems it's right!!   A guy from the sun said as the following: " I did find a few snippits here and there, These are from Troubleshooting Guide for Java SE 6 with HotSpotVMIn theHotSpot impleme...

狼之梦---爱乌及屋 607

防止stack buffer overflows攻击的方法 : ShadowCallStack

1、介绍 ShadowCallStack 是一个检测通道,目前只为 aarch64 实现,它可以保护程序免受返回地址覆盖(例如堆栈缓冲区溢出 : stack buffer overflows)。它的工作原理是将函数的返回地址保存到函数序言中单独分配的“shadow call stack”,子函数并从函数结语中的shadow call stack加载返回地址。 2、用法 要启用 ShadowCallStack,只需将-fsanitize=shadow-call-stack 标志传递给编译和链接命令行。在 aa

baron-周贺贺-代码改变世界ctw 1418

box-shadow妙用

1、模拟多边框 值得注意的是,阴影并不占有实际位置,所以显示如图,但是可以通过margin达到想要的效果

qq_38340907的博客 170

在LLVM中使用垃圾收集

原文:http://llvm.org/docs/GarbageCollection.html 摘要 本文论及如何将LLVM整合进一个支持垃圾收集语言的编译器。注意LLVM本身不支持垃圾收集。你必须自己提供。 快速开始 首先,你应该选择一个收集器策略。LLVM包括若干内置策略,但你还可以一个定制的定义来实现一个可载入的插件。注意收集器策略是一个LLVM应该如何生成代码,使它与你的收集器及运行

wuhui_gdnt的专栏 3088
上一篇: LWN:OpenSUSE MicroOS Desktop - 基于Flatpak 的不可更改发行版!
下一篇: LWN:Ubuntu 不再缺省提供Flatpak!
LinuxNews搬运工
博客等级 码龄7年 419粉丝 20原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值