您好,欢迎来到三六零分类信息网!老站,搜索引擎当天收录,欢迎发信息
免费发信息
三六零分类信息网 > 百色分类信息网,免费分类信息发布

与 EIP-1884 相关的安全考量

2022/9/2 21:34:53发布49次查看
背景唯有通力合作,我们才能将手游传奇发布网的价值发挥出来,供应市场的发展需要。
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-librarycom 上的、简化的 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 == stagesstarted);
require(cfgmindepositrequired <= msgvalue && msgvalue <= cfgmaxdepositrequired);
if(donatelist[msgsender] == false) {
if(transporter != address(0) && msgsender == transporter) {
validate msgdata
if(msgdatalength > 0) {
init new game
processdeposit(bytestoaddress(msgdata));
}
else {
emit logger("thank you for your contribution!", msgvalue);
}
}
else {
init new game
processdeposit(msgsender);
}
}
else {
emit logger("thank you for your contribution!", msgvalue);
}
}
这份代码的最后一次调用是在 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(msgsender));
logethreceived(msgsender, msgvalue);
}
重要提醒:上述的 crowdsales 合约并没有从根本上被破坏,只是调用者需要添加超过 2300 gas 来参与该 ico 合约。
chain security 分析
来自 chainsecurity 的 hubert ritzdorf 对近期的交易执行了分析。该分析基于主网上发生的实际交易,然后观察哪些交易会在 sload 操作码 gas 耗费了提升到 800 的时候失败。部分结果在此处 可见。
要点已在此处列明,附带下述评论:
前两种情况发生得更为频繁,其它的则不怎么常见。我们列出了最后一项,虽然在 eip1884 实施后它仍能工作,但我们不确定这么 “深” 的交易的 gas 值在当前是如何确定的。我们希望引起大家对潜在问题的警惕。
kyber network
function() public payable {require(reservetype[msgsender] != reservetypenone); etherreceival(msgsender, msgvalue);}
· kybernetwork 符合了这里所列的多个条件
· 合约 “指定了发送者”
· 主要通过其它合约来调用,这些调用依赖于 transfer 函数(限制在了 2300 gas)
我们联系了 kybernetwork,虽然免不了有些繁琐的工作要做,但问题是可以解决的
技术上来说,做市商只需要部署新的储备合约
cappedvault
function total() public view returns(uint) {
return getbalance() + withdrawn;
}
function () public payable {
require(total() + msgvalue <= 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
百色分类信息网,免费分类信息发布

VIP推荐

免费发布信息,免费发布B2B信息网站平台 - 三六零分类信息网 沪ICP备09012988号-2
企业名录