加载中 ...

引介 | 与 EIP-1884 相关的安全考量

2019-09-15 01:45 编辑:btc268.com 来源:区块链资讯

背景


  EIP 1884 已被纳入即将到来的以太坊 “伊斯坦布尔” 硬分叉中,它将:

将 SLOAD 操作码的 Gas 消耗量从 200 提高到 800将 BALANCE 和 EXTCODEHASH 的 Gas 消耗量从 400 提高到 700加入一种新的操作码 SELFBALANCE,Gas 消耗量是 5背后的理由是,因为状态数据大小的增长以及(相应的)从硬盘中取出状态树的额外读写开销,SLOAD、BALANCE 和 EXTCODEHASH 这几个操作码已经变得 “太过便宜” 了(对一个节点执行的实际工作量而言)。操作码的 Gas 消耗量与底层计算开销的严重不匹配,可能会导致多种问题、埋下网络攻击的种子(就像 2017 年的 “上海攻击” 那样)。
潜在问题


总的来说,给操作码重定 Gas 耗费,总是有可能会破坏那些明确依赖 “Gas 消耗量永远不变” 假设的合约。一直以来大家都认为操作码重定价是一个糟糕的办法,尤其是,一些主要的操作码 已经 在 Tangerine Whistle 中重新定价过了,那时候 SLOAD 的 Gas 耗费从 50 提高到了 200。不过,在某些情况下还可能出现更严重的问题:default 函数。
默认函数所谓 default 函数,就是一种合约用来处理无数据调用的方法 —— 用来处理那些完全没有明确调用任何方法的 ETH 转账。它们一般会被来创建一个 eVENt(使用 LOG 操作),然后外部系统就可以探测到这个事件,然后做出相应的操作(例如登记一笔交易已经完成)。一笔给合约的普通 ETH 转账总是会给接收者至少 2300 gas 作为 “津贴”。这笔 Gas 刚刚好可以让接收者能够发布一个事件,但又不足以让接收者能够更改状态(比如发起另一笔转账或者更新一个存储槽)。
EIP 1884 与默认函数EIP-1884 可能导致的问题就是,用 2300 gas 调用 default 函数可能会失败,例如因为以下原因:钱包受限:合约仅在 balance(self) 低于一个确定的下限时才接受支付发送者被指定:合约仅接受来自一组预先许可的发送者的支付功能受限:合约仅在一个特定变量(也就是一个 slot)为真时才接受支付现在,如果 default 函数在 2300 gas 的条件下停止工作,基本上不会有什么问题。例如,如果调用者是一个 EOA(外部所有者账户,也就是终端用户),调用者可以保证在一笔交易中附带比 21000 gas 多一点点的 gas。但别的地方可能出问题,例如:目标账户指定了发送者;发送者是智能合约,而且编程好了只用 transfer,不会附上额外的 gas在这种情况下,从发送者到目标账户的 ether 会永久丢失、无法复原,除非有其它机制来处理这种情况(比如替换掉发送者)。
调查


我联系了 EthSecurity 社区来帮助研究这种情况。要点如下:没有 payable 默认函数的合约不会受影响当前用 2300 gas 无法运行其默认函数的合约不会受影响,例如,在默认函数里操作 SLOAD 或者转移 ether 的合约
CONTract Library 分析来自 Contract Library 的 Neville Grech,对部分反编译的主网合约做了静态分析。这一分析覆盖了 95% 的主网合约、测试网最近 50 万个块上的合约,40 万份各异的字节码,然后列出了那些可能被影响的合约。分析可以在此处获取,并且会自动更新。注意,静态分析是一种无需执行程序便可分析所有程序行为的技术。该静态分析是根据下列部署在 contract-library.com 上的、简化的 datalog 技术规范来编写的。% 约束那些从可能的路径到 fallback 函数的例外情况FallbackFunctionBlockEdge(from, to) :- GlobalBlockEdge(from, to), InFunction(from, f), FallbackFunction(f), InFunction(to, g), FallbackFunction(g).
% 使用常规的 gas 语义分析 fallback 函数路径% 取最短的路径GasCostAnalysis = new CostAnalysis( Block_Gas, FallbackFunctionBlockEdge, 2300, min).
% 用升级后的 gas 语义分析 fallback 函数路径% 取最短的路径EIP1884GasCostAnalysis = new CostAnalysis( EIP1884Block_Gas, FallbackFunctionBlockEdge, 2300, min).
FallbackWillFailAnyway(n - 2300) :- GasCostAnalysis(*, n), n > 2300.
% 使用额外的 n - m 单位的 gas 后,fallback 函数会失败EIP1884FallbackWillFail(n - m) :- EIP1884GasCostAnalysis(block, n), n > 2300, GasCostAnalysis(block, m), !FallbackWillFailAnyway(*).该分析测算了 fallback 函数中的所有可能路径的 Gas 消耗量,使用了 EIP-1884 部署前后的 Gas 设定。如果某个路径可以在旧的 gas 语义下完成,但无法在新的语义下完成,我们就抓出相应的合约。
该分析自动抓取出了主网上的 200 个合约,包括 Kyber Network 的合约和 CappedVault 合约。注意,如果 BALANCE 操作码的 gas 要求稍低一点(比如是 600),CappedVault 合约就还是能正常工作。该分析也发现了多个其它(带余额的)合约会在新的 gas 设定的多种条件下出错:EbcFund 合约中储存了超过 580 个 eth,在低于 2300 gas 的条件下将不再能接受捐献。/** * @dev fallback function to send ether to smart contract **/ function () public payable { require(currentStage == Stages.Started); require(cfgMinDepositRequired <= msg.value && msg.value <= cfgMaxDepositRequired); if(donateList[msg.sender] == false) {if(transporter != address(0) && msg.sender == transporter) {//validate msg.dataif(msg.data.length > 0) {//init new game processDeposit(byteSTOAddress(msg.data)); }else {emit Logger("Thank you for your contribution!.", msg.value); } }else {//init new game processDeposit(msg.sender); } }else {emit Logger("Thank you for your contribution!", msg.value); } }这份代码的最后一次调用是在 144 天以前。
NEXXO crowdsale 合约也是一样: modifier onlyICO() {require(now >= icoStartDate && now < icoEndDate, "CrowdSale is not running"); _; }
function () public payable onlyICO{require(!stopped, "CrowdSale is stopping"); }NEXXO 会检查三个存储槽,icoStartDate、icoEndDate 以及 stopped,在新的 gas 规则下总共需要 2400 gas。
Crowd Machine Compute Token crowdsale 合约也是一样的问题: modifier onlyIfRunning {require(running); _; }
function () public onlyIfRunning payable {require(isApproved(msg.sender)); LogEthReceived(msg.sender, msg.value); }重要提醒:上述的 crowdsales 合约并没有从根本上被破坏,只是调用者需要添加超过 2300 gas 来参与该 ICO 合约。

Chain Security 分析来自 ChainSecurity 的 Hubert Ritzdorf 对近期的交易执行了分析。该分析基于主网上发生的实际交易,然后观察哪些交易会在 SLOAD 操作码 Gas 耗费了提升到 800 的时候失败。部分结果在此处 可见。要点已在此处列明,附带下述评论:前两种情况发生得更为频繁,其它的则不怎么常见。我们列出了最后一项,虽然在 EIP1884 实施后它仍能工作,但我们不确定这么 “深” 的交易的 gas 值在当前是如何确定的。我们希望引起大家对潜在问题的警惕。
Kyber Networkfunction() public payable {require(reserveType[msg.sender] != ReserveType.NONE); EtherReceival(msg.sender, msg.value); }   KyberNetwork 符合了这里所列的多个条件合约 “指定了发送者”主要通过其它合约来调用,这些调用依赖于 transfer 函数(限制在了 2300 gas)我们联系了 KyberNetwork,虽然免不了有些繁琐的工作要做,但问题是可以解决的技术上来说,做市商只需要部署新的储备合约
CappedVaultfunction total() public view returns(uint) {return getBalance() withdrawn; }
function () public payable {require(total() msg.value <= limit); }在这个合约中,withdrawn 是一个存储槽, limit 也是。
CappedVault 存有超过 4000 个 ether 和 7 万笔内部交易,同样符合下列条件:功能受限模式使用了两次 SLOAD 和一次 BALANCE实现细节:该合约被编写成,只要发送给该合约的 ether 总数超过 33333,合约就 “断开”。也就是说,不管合约中当前有多少 ether,只要发送给合约的 ether 总数超过 3 万 3 千,就不再接受 ether。这就意味着已经有机制来处理默认函数停止运行的情况了。limit 是一个存储 slot,但也可以实现为编译时常量(compile-time constant),从而省下一个 SLOAD。balance(self) 在伊斯坦布尔分叉后可重写为 SELFBALANCE所以本质上,当前的用量是:200 (sload limit) 200 (sload withdrawn) 400 (balance) = 800 gas而在 EIP-1884 部署后:
5 (selfbalance) 800 (sload withdrawn) = 805 gas

  (完)

  (文内提供了许多超链接,请点击阅读原文到 EthFans 网站上获取)

  原文链接:

  https://github.com/holiman/eip-1884-security

  作者: holiman

  翻译: 阿剑

  你可能还喜欢:

干货 | 以太坊中的账户、交易、Gas和区块Gas Limit科普 | 深处的蚁穴:与 Gas 相关的三种安全问题

关键词:比特币新闻 币牛牛

转载自比特币新闻网(www.btc268.com),提供比特币行情走势分析与数字货币投资炒币最新消息。

原文标题:引介 | 与 EIP-1884 相关的安全考量

原文地址:http://www.btc268.com/ytf/xw/15311.html

本文来源:区块链资讯编辑:btc268.com

本文仅代表作者个人观点,与本网站立场无关。

本网站转载信息目的在于传递更多信息。请读者仅作参考,投资有风险,入市须谨慎!

'); })();