以太坊暫未修復的一個bug 數組越界

以太坊 區塊鏈 以太坊節點 程序新視界 2018-07-25

前些天朋友遇到一個關於以太坊使用的leveldb導致的數組越界問題,一起討論了很久。如果大家持續使用以太坊節點,遲早也會遇到此問題,在本篇文章中給大家分析一下,做好提前準備。

前些天朋友遇到一個關於以太坊使用的leveldb導致的數組越界問題,一起討論了很久。如果大家持續使用以太坊節點,遲早也會遇到此問題,在本篇文章中給大家分析一下,做好提前準備。

以太坊暫未修復的一個bug 數組越界

異常信息

我們先看一下具體的異常信息,對於普通的異常重啟geth節點即可解決,但如果遇到下面這個異常信息,重啟或升級版本都是無法解決的。

INFO [04-28|10:03:35] Starting peer-to-peer node               instance=Geth/v1.7.3-stable/linux-amd64/go1.9INFO [04-28|10:03:35] Allocated cache and file handles         database=/mnt/data/eth/geth/chaindata cache=128 handles=1024panic: runtime error: index out of rangegoroutine 1 [running]:github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb.shortenb(0x10040ff1126, 0x4, 0xc4204bf9f8)        /mnt/go/src/github.com/ethereum/go-ethereum-1.7.3/build/_workspace/src/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/util.go:30 +0x14dgithub.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb.(*version).computeCompaction(0xc4416120f0)        /mnt/go/src/github.com/ethereum/go-ethereum-1.7.3/build/_workspace/src/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/version.go:395 +0x4b3github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb.(*versionStaging).finish(0xc4204bfd18, 0xc4201e8000)        /mnt/go/src/github.com/ethereum/go-ethereum-1.7.3/build/_workspace/src/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/version.go:510 +0x935github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb.(*version).spawn(0xc420182230, 0xc4201e8000, 0xc420182230)        /mnt/go/src/github.com/ethereum/go-ethereum-1.7.3/build/_workspace/src/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/version.go:279 +0x7agithub.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb.(*session).commit(0xc4201d0240, 0xc4201e8000, 0x0, 0x0)        /mnt/go/src/github.com/ethereum/go-ethereum-1.7.3/build/_workspace/src/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/session.go:195 +0x88github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb.(*DB).recoverJournal(0xc4200ee600, 0xc4200ee600, 0xc420068660)        /mnt/go/src/github.com/ethereum/go-ethereum-1.7.3/build/_workspace/src/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/db.go:538 +0xdb8github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb.openDB(0xc4201d0240, 0x0, 0x0, 0xc4201d0240)        /mnt/go/src/github.com/ethereum/go-ethereum-1.7.3/build/_workspace/src/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/db.go:122 +0x6bagithub.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb.Open(0x185e540, 0xc4202047e0, 0xc4204c02f0, 0x0, 0x0, 0x0)        /mnt/go/src/github.com/ethereum/go-ethereum-1.7.3/build/_workspace/src/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/db.go:194 +0x100github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb.OpenFile(0xc4201f7ba0, 0x1c, 0xc4204c02f0, 0xc4201c5bb0, 0x4, 0x4)        /mnt/go/src/github.com/ethereum/go-ethereum-1.7.3/build/_workspace/src/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/db.go:216 +0x97github.com/ethereum/go-ethereum/ethdb.NewLDBDatabase(0xc4201f7ba0, 0x1c, 0x80, 0x400, 0x1c, 0x0, 0x0)        /mnt/go/src/github.com/ethereum/go-ethereum-1.7.3/build/_workspace/src/github.com/ethereum/go-ethereum/ethdb/database.go:72 +0x363github.com/ethereum/go-ethereum/node.(*ServiceContext).OpenDatabase(0xc4202a2da0, 0xf4ad6c, 0x9, 0x80, 0x400, 0x0, 0x0, 0x0, 0x0)        /mnt/go/src/github.com/ethereum/go-ethereum-1.7.3/build/_workspace/src/github.com/ethereum/go-ethereum/node/service.go:46 +0x133github.com/ethereum/go-ethereum/eth.CreateDB(0xc4202a2da0, 0xc4203f4800, 0xf4ad6c, 0x9, 0x0, 0x0, 0x0, 0x0)        /mnt/go/src/github.com/ethereum/go-ethereum-1.7.3/build/_workspace/src/github.com/ethereum/go-ethereum/eth/backend.go:201 +0x5dgithub.com/ethereum/go-ethereum/eth.New(0xc4202a2da0, 0xc4203f4800, 0x181d560, 0xc4204c5808, 0x417268)        /mnt/go/src/github.com/ethereum/go-ethereum-1.7.3/build/_workspace/src/github.com/ethereum/go-ethereum/eth/backend.go:111 +0x93github.com/ethereum/go-ethereum/cmd/utils.RegisterEthService.func2(0xc4202a2da0, 0xc4204b4420, 0xc4204c5b18, 0x0, 0xc4204b4450)        /mnt/go/src/github.com/ethereum/go-ethereum-1.7.3/build/_workspace/src/github.com/ethereum/go-ethereum/cmd/utils/flags.go:1065 +0x3dgithub.com/ethereum/go-ethereum/node.(*Node).Start(0xc4201f4480, 0x0, 0x0)        /mnt/go/src/github.com/ethereum/go-ethereum-1.7.3/build/_workspace/src/github.com/ethereum/go-ethereum/node/node.go:175 +0x433github.com/ethereum/go-ethereum/cmd/utils.StartNode(0xc4201f4480)        /mnt/go/src/github.com/ethereum/go-ethereum-1.7.3/build/_workspace/src/github.com/ethereum/go-ethereum/cmd/utils/cmd.go:62 +0x2fmain.startNode(0xc420230840, 0xc4201f4480)        /mnt/go/src/github.com/ethereum/go-ethereum-1.7.3/build/_workspace/src/github.com/ethereum/go-ethereum/cmd/geth/main.go:225 +0x43main.geth(0xc420230840, 0xffbbe0, 0xb2d05e00)        /mnt/go/src/github.com/ethereum/go-ethereum-1.7.3/build/_workspace/src/github.com/ethereum/go-ethereum/cmd/geth/main.go:215 +0x43github.com/ethereum/go-ethereum/vendor/gopkg.in/urfave/cli%2ev1.HandleAction(0xdd0280, 0xffd068, 0xc420230840, 0xc420230840, 0xc4204c5f40)        /mnt/go/src/github.com/ethereum/go-ethereum-1.7.3/build/_workspace/src/github.com/ethereum/go-ethereum/vendor/gopkg.in/urfave/cli.v1/app.go:490 +0xd2github.com/ethereum/go-ethereum/vendor/gopkg.in/urfave/cli%2ev1.(*App).Run(0xc420250000, 0xc4200100e0, 0xe, 0xe, 0x0, 0x0)        /mnt/go/src/github.com/ethereum/go-ethereum-1.7.3/build/_workspace/src/github.com/ethereum/go-ethereum/vendor/gopkg.in/urfave/cli.v1/app.go:264

異常分析

先查看一下異常信息的第一段代碼位置:

/mnt/go/src/github.com/ethereum/go-ethereum-1.7.3/build/_workspace/src/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/util.go:30

進入此源代碼之後,能夠看到如下代碼:

var bunits = [...]string{"", "Ki", "Mi", "Gi"}func shortenb(bytes int) string {    i := 0    for ; bytes > 1024 && i < 4; i++ {        bytes /= 1024    }    return fmt.Sprintf("%d%sB", bytes, bunits[i])}

其中異常就發生在return代碼部分,也就是通過bunits[i]獲取數據時,i的值超出了bunits數組的範圍。

看這段代碼,當shortenb傳入的bytes<1024 * 1024 * 1024是沒問題的,i <= 3。但是,當bytes>1024 * 1024 * 1024 * 1024時,也就是單位到TB的時候,i的值將等於4,此時將發生數組越界異常。

為什麼剛才說大家遲早會遇到這個問題呢,就是當我們同步區塊鏈數據一開始就使用full或者很早就採用full模式的話,數據量很快會到達TB級別,而leveldb的這段代碼,當到達TB級別之後就會出現數組越界異常。

問題解決方案

上面已經分析了問題的原因,那麼怎麼解決這個問題呢?將數組bunits再擴展一個“Ti”項?這樣修改不敢打包票會修復問題,因為只是在數組裡面添加一個類型,不確定其他地方是否能夠使用此類型。如果要這樣修改,可能需要通讀相關的代碼,然後測試驗證才可以。

另外一種比較輕量級的改動是將for循環中i<4的判斷修改為i<3,修改後的代碼為:

var bunits = [...]string{"", "Ki", "Mi", "Gi"}func shortenb(bytes int) string {    i := 0    for ; bytes > 1024 && i < 3; i++ {        bytes /= 1024    }    return fmt.Sprintf("%d%sB", bytes, bunits[i])}

這樣再拿上面bytes>1024 * 1024 * 1024 * 1024計算一下,當單位編程TB的時候,會使用1024GB,符合原來數組的最大單位。

PS:當然,修改之後大家是需要進行相應級別數據量的測試驗證的。

以太坊暫未修復的一個bug 數組越界
本文來源: 程序新視界 文章作者: 程序新視界 我要糾錯
聲明:本文由入駐金色財經的作者撰寫,觀點僅代表作者本人,絕不代表金色財經贊同其觀點或證實其描述。
比特幣實時價格 ¥55770.23(數據來源:火幣Pro)

相關推薦

推薦中...