d doeda-zogt.xyz
doeda-zogt.xyz · TIMES
All The News, Wisdom & Knowledge
VOL. MMXXVI · doeda-zogt.xyz · gasyou-hua-lou-dong-an-li
特别报道 · SPECIAL REPORT

Gas优化漏洞案例复盘:那些被节省的Gas,最后变成了多少损失

通过整理近年Gas优化漏洞案例,剖析存储槽冲突、unchecked溢出、汇编内联越界、循环短路等典型问题的成因、影响与修复方式,为合约开发者敲响节省Gas与守住安全的双线警钟。

发布于 2026-05-24T06:12:20.346225+00:00更新于 2026-05-24T14:42:01.875944+00:00
Gas优化漏洞案例 - Gas优化漏洞案例复盘:那些被节省的Gas,最后变成了多少损失
图:本报记者摄 · 关于「Gas优化漏洞案例复盘:那些被节省的Gas,最后变成了多少损失」的视觉记录

Gas优化漏洞案例复盘

在以太坊与 Binance 智能链上,Gas 优化几乎是每个项目方都会做的事。但有些「节省」事后看来代价惨重。本文整理几起代表性的 Gas优化漏洞案例,按成因归类,帮助团队建立更稳健的优化纪律。

案例一:存储槽冲突导致代理合约状态错乱

某 DeFi 项目为了压缩存储成本,把多个状态变量塞进同一个 uint256 槽位。代理升级后,新版本逻辑合约错误地把第 0 槽当作普通变量使用,而第 0 槽实际上是 ERC-1967 规定的 implementation slot。结果,攻击者通过一笔交易就把代理指向恶意逻辑合约。

教训:任何位压缩都必须配合显式的存储布局图(storage layout),并在 CI 中比对升级前后两个版本的布局。

案例二:unchecked 包裹用户输入引发铸币漏洞

一个 NFT 项目把铸币函数中的所有算术运算都用 unchecked 块包裹,目的是省下 0.8 默认的溢出检查 Gas。结果,攻击者传入一个接近 type(uint256).max 的 amount,绕过总量限制,铸出无数 NFT。

教训:unchecked 仅适用于「数学上可证明不会溢出」的循环计数与内部簿记,绝不应套在用户可控的算术上。在 B安 生态项目中,类似漏洞曾导致单笔损失超百万美元。

案例三:汇编内联未更新 free memory pointer

一段 inline assembly 把 calldata 拷贝到内存以构造 ABI 调用,但忘记把 0x40 处的 free memory pointer 往后推。随后的 Solidity 代码在写入新数据时,覆盖了刚才拷贝出来的关键参数。攻击者借此构造了一个具备欺骗性返回值的代理调用。

教训:所有手写 Yul 都必须显式管理 free memory pointer,并在审计时逐行验证。

案例四:循环展开漏掉边界判断

某项目把核心循环展开成 8 条重复代码块,用以减少跳转 Gas。然而当数组长度不是 8 的整数倍时,最后一段补丁代码出现越界读取,导致 keccak 哈希被污染,最终触发签名验证失败。

教训:循环展开必须保留 fallback 的「剩余项处理」逻辑,并通过 fuzz 测试覆盖 0、1、7、8、9 等典型长度。

案例五:自定义错误覆盖率不足

为节省字节码,团队把所有 require 改成 custom error,但前端 SDK 未同步升级。普通用户的失败交易显示为「unknown error」,无法理解问题原因;运营人员也无法基于日志做异常告警。最终引发大量误操作和资金锁定。

教训:在 BN必安 等多链部署的场景中,custom error 的发布需要前后端 + 监控同步上线,否则节省的不是 Gas,而是用户的信心。

案例六:view 函数被错误标记为 pure

开发者为追求编译器优化,把一个读取链上状态的函数标成 pure。最终在调用时报错,但因为该函数被嵌套在一个支付路径中,导致大量用户交易直接 revert,前端却展示「Gas 不足」误导信息。

教训:函数可见性与可变性修饰符,是 Solidity 安全模型的核心组件,不应为优化让步。

结语:每一笔节省,都要配一份证明

看完以上 Gas优化漏洞案例,可以发现共同点都不是「优化本身错了」,而是「优化的边界没有被证明」。在做下一次 Gas 优化前,先回答三个问题:能在 fuzz 测试中坚持多少轮?能否被任意外部输入打破?升级时是否兼容旧布局?答得清楚,再动手。