You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何函数返回地址采用栈存储?能否用独立内存区提升安全性?

函数返回地址存栈的原因,以及分离存储的可行性分析

为什么返回地址要存在栈里?

  • 硬件设计的历史惯性:早期x86这类主流CPU的指令集,直接把栈作为函数调用的核心载体。call指令执行时自动把下一条指令的地址(也就是返回地址)压入栈,ret指令又自动从栈顶弹出地址跳转回去。这种设计完全由硬件支持,不需要额外的内存管理逻辑,函数调用的开销能压到最低。
  • 完美适配嵌套/递归调用:栈是后进先出的结构,刚好对应函数嵌套调用的顺序——你调了A函数,A又调B,B执行完要先回到A,A再回到主函数,栈的结构天然就能按这个顺序管理返回地址。要是换个独立存储区,还得额外维护调用链的顺序,比如搞个链表,这会凭空增加运行时的复杂度和性能开销。
  • 上下文管理更高效:栈帧里不光有返回地址,还有局部变量、寄存器备份这些上下文信息。把返回地址和这些存在一块儿,函数调用/返回时的上下文保存、恢复操作能一次性完成,效率比分开存高多了。

能不能用独立内存区存返回地址来提升安全性?

其实现在已经有成熟的技术实现类似的隔离效果,只是不是完全和栈割裂:

  • 硬件级方案:影子栈:比如Intel的CET技术、ARM的PAC机制,就是专门开一块受硬件保护的内存区域来存返回地址,和普通栈彻底分开。普通栈发生缓冲区溢出时,根本碰不到影子栈里的返回地址,能直接挡住绝大多数控制流劫持攻击。
  • 软件级方案:控制流完整性(CFI):像Clang的CFI、GCC的相关扩展,通过编译时的静态分析标记合法的返回地址,运行时检查跳转的目标是否合法,相当于间接把返回地址的验证逻辑和普通栈操作隔离开,就算栈里的返回地址被篡改,也会被检测到并终止程序。

但如果真要搞一个完全和栈无关联的独立返回地址存储区,会面临几个现实问题:

  • 性能损耗:每次函数调用和返回都要在两个内存区域来回操作,还要维护调用链的对应关系,会多出来不少指令开销,对于嵌入式系统、高性能计算这类对延迟敏感的场景,这个代价很难接受。
  • 兼容性噩梦:现在几乎所有的程序、编译工具链都是基于“返回地址存栈”的设计来做的,彻底改动的话,现有代码要么全重写,要么工具链全重构,生态兼容成本高到离谱。
  • 仍有攻击可能:就算返回地址存在独立区域,攻击者要是能通过其他漏洞(比如权限提升漏洞)篡改这个区域的保护属性,或者绕过验证逻辑,还是能实现攻击,只是门槛更高而已。

总的来说,返回地址存栈是历史硬件设计和性能权衡的结果,而现在的安全技术已经通过影子栈、CFI这类方案,实现了返回地址的安全隔离,既兼顾了兼容性和性能,又有效解决了缓冲区溢出带来的风险。

内容的提问来源于stack exchange,提问作者Mach

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 03:45:10