先日セキュリティーアラートが上がり、多くの取引所が一時ERC20準拠トークンの取引を停止したという報道とともに話題になった、BatchOverFlowについて今更な感じですが自分なりにまとめてみます。
今回の問題についてはGunosyの@yamarkzさんがいち早くまとめていましたので、そちらに詳細の説明は譲ります。
blockchain.gunosy.io
本ブログでは前々から使ってみたかったEthFiddle.comを使いこのバグを体験できる擬似コードと、またMythrilを用いての危険なコードの機械的な検証がどの程度有効なのかについてまとめてみたいと思います。
BatchOverFlowを体験してみる
EthFiddle.comで今回問題になったERC20Tokenを簡単に実装してみました。これは@yamarkzさんがまとめてくださったgitterのコードを含めただけの擬似的な実装です。
「Compile and Run」をクリックしてEthFiddle.comのページを表示することで実際にコントラクトを動かすことができますのでぜひ試してみてください。
ethfiddle.com
操作説明
今回のBatchOverFlowを実際に起こすための操作方法を説明します。
1.BatchOverFlowTokenをデプロイする
まずはTokenをデプロイします。
BatchOverFlowTokenをデプロイしてください。それ以外のContractはデプロイする必要はありません。
2.batchTransferメソッドをcallする
今回の原因であるbatchTransferメソッドをcallします。callボタンを押すとパラメータを設定する画面が表示されます。
3.Addressを設定する
_receiversをクリックするとアドレスの一覧が表示されるので、2番目と3番目の2つのアドレスをそれぞれ設定します。
addressを複数設定するには複数回選択すればOKです。選択すると前に選択したaddressを保持して新しいaddressを追加してくれます。
この辺のEthFiddle.comのUI設計はとても親切
4. valueを設定して実行する
_valueに
0x8000000000000000000000000000000000000000000000000000000000000000
を入力し、CallをクリックしてbatchTransferメソッドを実行します。
5.balanceOfを実行して残高を確認
balanceOfメソッドをcallして_ownerにbatchTransferに指定したアドレスを入力して実行します。
実行結果で表示される値が非常に大きな値になってるのが確認できます。
また、totalSupplyよりも大きな値になっていることも確認できます。
Mythrilでのチェック
Solidityコードの安全性をチェックしてくれるツールであるMythrilで上記の擬似コードをチェックしてみました。
github.com
結果は以下の通りです。
$ myth -x contracts/BatchOverFlowToken.sol:BatchOverFlowToken ==== Exception state ==== Type: Informational Contract: BatchOverFlowToken Function name: batchTransfer(address[],uint256) PC address: 933 A reachable exception (opcode 0xfe) has been detected. This can be caused by type errors, division by zero, out-of-bounds array access, or assert violations. This is acceptable in most situations. Note however that `assert()` should only be used to check invariants. Use `require()` for regular input checking. -------------------- In file: contracts/BatchOverFlowToken.sol:79 _receivers[i] -------------------- ==== Exception state ==== Type: Informational Contract: BatchOverFlowToken Function name: transfer(address,uint256) PC address: 1747 A reachable exception (opcode 0xfe) has been detected. This can be caused by type errors, division by zero, out-of-bounds array access, or assert violations. This is acceptable in most situations. Note however that `assert()` should only be used to check invariants. Use `require()` for regular input checking. -------------------- In file: contracts/BatchOverFlowToken.sol:128 assert(c >= a) -------------------- ==== Integer Overflow ==== Type: Warning Contract: BatchOverFlowToken Function name: transfer(address,uint256) PC address: 1734 A possible integer overflow exists in the function `transfer(address,uint256)`. The addition or multiplication may result in a value higher than the maximum representable integer. -------------------- In file: contracts/BatchOverFlowToken.sol:127 a + b -------------------- ==== Integer Overflow ==== Type: Warning Contract: BatchOverFlowToken Function name: batchTransfer(address[],uint256) PC address: 529 A possible integer overflow exists in the function `batchTransfer(address[],uint256)`. The addition or multiplication may result in a value higher than the maximum representable integer. -------------------- In file: contracts/BatchOverFlowToken.sol:69 uint256(cnt) * _value --------------------
69行目の危ないコードをしっかりと補足して通知してくれています。(2byte文字があるとレポート表示のコード部分がうまく表示できないようなので、実際に試す場合は日本語コメントをすべて削除してからMythrilを実行してください。)