今日运势评分

1

本月运势

丁丑月

震荡偏多

甲庚相冲,辰土劫财
己不破券二比并亡
卯不穿井水泉不香

开市,交易,立券,纳财,开池,补垣,嫁娶,纳采,纳畜,取渔,安床

修造,上梁,入宅,祈福,探病,掘井,动土,安门,安葬,作灶

月相

冲顶回落

日冲

Powered by RitMEX

PEPE0.00 3.62%

SUI5.20 9.18%

TON5.37 3.41%

TRX0.24 1.66%

DOGE0.33 3.99%

XRP2.35 2.55%

SOL188.56 0.88%

BNB693.51 0.79%

ETH3276.54 1.44%

BTC94789.90 2.49%

ETH Gas4.08 Gwei

中性
50

Solidity 编译器中高危漏洞:误删状态变量赋值

本文从源代码层面详解介绍了 Solidity( 0.8.13<=solidity<0.8.17)编译器在编译过程中,因为 Yul 优化机制的缺陷导致的状态变量赋值操作被错误删除的中/高漏洞原理及相应的预防措施。

本文从源代码层面详解介绍了 Solidity( 0.8.13<=solidity<0.8.17)编译器在编译过程中,因为 Yul 优化机制的缺陷导致的状态变量赋值操作被错误删除的中/高漏洞原理及相应的预防措施。

帮助合约开发人员提高合约开发时的安全意识,有效规避或缓解 SOL-2022-7 漏洞对合约代码安全性的影响。

漏洞详情

Yul 优化机制是 Solidity 编译合约代码的可选项,可以通过优化机制减少合约中某些冗余的指令,从而降低合约部署和执行过程中的 gas 费用,具体的 Yul 优化机制可以参考官方文档

在编译过程的 UnusedStoreEliminator 优化步骤中,编译器会将“冗余”的 Storage 写入操作移除,但由于对“冗余”的识别缺陷,当某个 Yul 函数块调用特定的用户定义函数(函数内部存在某个分支不影响调用块的执行流),且在该 Yul 函数块中被调用函数前后存在对同一状态变量的写入操作,会导致在 Yul 优化机制将块中该用户定义函数被调用前的所有的 Storage 写入操作从编译层面被永久删除。

考虑如下代码:

contract Eocene {

uint public x;

function attack() public {

x = 1;

x = 2;

}

}

在 UnusedStoreEliminator 优化时,x= 1 显然对于函数 attack()的整个执行是冗余的。自然的,优化后的 Yul 代码会将 x= 1;删除来降低合约的 gas 消耗。

接下来考虑在中间插入对自定义函数调用:

contract Eocene {

uint public x;

function attack(uint i) public {

x = 1;

y(i);

x = 2;

}

function y(uint i) internal{

if (i > 0){

return;

}

assembly { return( 0, 0) }

}

}

显然,由于 y()函数的调用,我们需要判断 y()函数是否会影响函数 attack()的执行,如果 y()函数可以导致整个函数执行流终止(注意,不是回滚,Yul 代码中的 return()函数可以实现),那么 x= 1 显然是不能删除的,所以对于上面的合约来说由于 y()函数中存在 assembly {return( 0, 0)}可以导致整个消息调用终止,x= 1 自然不能被删除。

但在 Solidity 编译器中,由于代码逻辑的问题,使得 x= 1 在编译时被错误的删除,永久改变了代码逻辑。

实际编译测试结果如下:

震惊!不应该被优化的 x= 1 的 Yul 代码丢了!欲知后事如何,请往下看。

在 solidiry 编译器代码的 UnusedStoreEliminator 中,通过 SSA 变量追踪和控制流追踪来判断一个 Storage 写入操作是否是冗余的。当进入一个自定义函数中时,UnusedStoreEliminator 如果遇到:

  1. memory 或 storage 写入操作:将 memory 和 storage 写入操作存储到 m_store 变量中,并将该操作的初始状态设置为 Undecided;

  2. 函数调用:获取函数的 memory 或 storage 读写操作位置,并和 m_store 变量中存储的所有 Undecided 状态下的操作进行对比:

  3. 如果是对 m_store 中存储操作的写入覆盖,则将 m_store 中对应的操作状态改为 Unused

  4. 如果是对 m_store 中存储操作的读取,则将对应 m_store 中的对应操作状态改为 Used

  5. 如果该函数没有任何可以继续执行消息调用的分支,将 m_store 中所有的内存写操作改为 Unused

  6. 在上诉条件下,如果函数可以终止执行流,将 m_store 中,状态为 Undecided 状态的 storage 写操作改为 Used;反之,标识为 Unused

  7. 函数结束:将所有标记为 Unused 的写入操作删除

对 memory 或 storage 写入操作的初始化代码如下:

可以看到,将遇到的 memory 和 storage 写入操作存储到 m_store 中

遇到函数调用时的处理逻辑代码如下:

其中,operationFromFunctionCall()和 applyOperation()实现上诉的 2.1 , 2.2 处理逻辑。位于下方的基于函数的 canContinue 和 canTerminate 进行判断的 If 语句实现 2.3 逻辑。

需要注意,正是下方的 If 判断的缺陷,导致了漏洞的存在!!!

operationFromFunctionCall()来获取该函数的所有 memory 或 storage 读写操作,这里需要注意,Yul 中存在很多的内置函数,例如 sstore(), return()。这里可以看到对于内置函数和用户定义函数有不同的处理逻辑。

而 applyOperation()函数则是将从 operationFromFuncitonCall()获取的所有读写操作进行对比,来判断存储到 m_store 中的是否在该次函数调用中被读写,并修改 m_store 中的对应的操作状态。

考虑上述的 UnusedStoreEliminator 优化逻辑对 Eocene 合约的 attack()函数的处理:

将 x= 1 存储操作到 m_store 变量中,状态设置为 Undecided

1.遇到 y()函数调用,获取 y()函数调用的所有读写操作

2.遍历 m_store 变量,发现 y()调用引起的所有读写操作和 x= 1 无关,x= 1 状态仍然是 Undecided

  • 获取 y()函数的控制流逻辑,因为 y()函数存在可以正常返回的分支,所以 canContinue 为 True,不进入 If 判断。x= 1 状态仍然为 Undecided!!!

3.遇到 x= 2 存储操作:

  • 遍历 m_store 变量,发现处于 Undecided 状态的 x= 1 ,x= 2 操作覆盖 x= 1 ,设置 x= 1 状态为 Unused。

  • 将 x= 2 操作存入 m_store,初始状态为 undecided。

4. 函数结束:

  • 将所有 m_store 中 undecided 状态的操作状态改为 Used

  • 将所有 m_store 中 Unused 状态的操作删除

显然,在调用函数时,如果被调用函数可以终止消息执行,应该将被调用函数前所有的 Undecided 状态的写入操作改为 Used,而不是依旧保留为 Undecided,导致位于被调用函数前的写入操作被错误的删除。

此外,需要注意的是,每个用户自定义函数控制流标识是会传递的,所以在多个函数递归调用的场景下,即便最底层函数满足上诉逻辑,x= 1 也有可能被删除。

Solidity中,举例了基本相同的逻辑下,不会受到影响的合约代码。但,该代码不受该漏洞的影响并不是因为 UnusedStoreEliminator 的处理逻辑存在其他可能,而是在 UnusedStoreEliminator 之前的 Yul 优化步骤中,存在 FullInliner 优化过程会将微小或只有一次调用的被调用函数,嵌入到调用函数中,避免了漏洞触发条件中的用户定义函数。

contract Normal {

uint public x;

function f(bool a) public {

x = 1;

g(a);

x = 2;

}

function g(bool a) internal {

if (!a)

assembly { return( 0, 0) }

}

}

编译结果如下:

函数 g(bool a)被嵌入到函数 f()中,避免了用户定义函数的漏洞条件,避免了漏洞的产生。

解决方案

最根本的解决方案是不使用在受影响范围的 solidity 编译器进行编译,如果需要使用漏洞版本的编译器,可以考虑在编译时去除 UnusedStoreEliminator 优化步骤。

如果想要从合约代码层面进行漏洞缓解,考虑到多个优化步骤的复杂性,以及实际函数调用流的复杂性,请寻找专业的安全人员进行代码审计来帮助发现合约中的因为该漏洞导致的安全问题。

欢迎加入深潮TechFlow官方社群

Telegram订阅群:https://t.me/TechFlowDaily
Twitter官方账号:https://x.com/TechFlowPost
Twitter英文账号:https://x.com/DeFlow_Intern
作者Eocene ResearchEocene Research
相关文章
2025.01.10 - 10 小时前
Crypto Content Creator Campus (C4) 推出 KOL 孵化学校,培养新一代加密内容创作者
C4 KOL 孵化学校的诞生源于我们坚信,每一位创作者都有潜力在加密货币领域脱颖而出。
2025.01.10 - 12 小时前
MOMO.FUN:AI驱动的 Meme+ DeFi平台,引领去中心化交易新未来
MOMO.FUN:让每一次创意都更有价值,让每一次交易都更有趣!
2025.01.10 - 15 小时前
Berachain 主网在即,生态 AI 赛道与潜力项目全景指南
探讨Berachain生态中的17个AI项目以及11个值得关注的项目。
2025.01.10 - 18 小时前
Aiccelerate DAO:加速去中心化开源 AI 发展
ai16z 联创 Shaw、Virtuals 联创 Ethermage、Story Protocol 联创 Jason 站台,Aiccelerate DAO 究竟有何魔力?
AI
2025.01.10 - 19 小时前
链游勇闯 AI Agent 赛道,游戏角色 Freya 何以走红并入围 Binance Alpha?
在其它知名链游项目入局之前,类 Roguelike 奇幻风格RPG游戏 Starfall Chronicles已带着其游戏中的“头牌”Freya率先勇闯AI代理领域,并在日前入围Binance Alpha。
2025.01.10 - 20 小时前
Base 生态 AI 大掘金:Virtuals 之外,全面盘点那些你可能忽略的宝藏项目们
试图寻找更多 Base 上的 AI Agent 相关项目。
2025.01.08 - 3 天前
Solayer Chain 速读:毫秒交易时代到来?
1 毫秒交易确认、100 Gbps,Solayer Chain 究竟是什么?
2025.01.08 - 3 天前
AI 领衔主演,22 个加密项目集体下场布局 AI Agent
这些项目主要通过技术框架开发和应用场景落地等方式来加速AI Agent生态的构建与发展。
2025.01.08 - 3 天前
盘点 Solana AI 黑客松 10 大 Crypto AI 项目
涵盖DeFi投资、开发工具、社交娱乐等多个领域。
2025.01.08 - 3 天前
ODOS 重新定义 DeFi 交易体验:一个平台三种玩法,满足所有交易需求
通过全面的产品套件,ODOS 为您带来卓越的掌控力、高效的交易体验以及显著的结果。