2024年10月,Bitcoin Core專案披露了一個阻斷服務(Denial-of-Service)漏洞,原因是inv-to-send集合變得過大,此漏洞由我發現,影響v25.0之前的Bitcoin Core版本。我想在此記錄下當時調查的一些筆記和截圖。2023年5月初,我的監控基礎設施注意到這個錯誤正在影響主網路節點,這讓我能夠精確地找出問題的根源。修復程式碼的功勞歸於Anthony Towns。
2023年5月2日,我注意到在我的其中一個監控節點上,連入連線從大約1901在兩天內下降到只有35個。通常,一個節點會保持其已滿的連入插槽,直到它重新啟動或失去網路連線。
與其他貢獻者交流後,我注意到該節點的CPU使用率為100%。這影響了節點,使其無法與其對等節點保持通訊,導致連入連線超時並斷開。在節點進程上使用perf top
,我可以看到大量的CPU時間花費在CTxMemPool::CompareDepthAndScore()
的b-msghand
執行緒中。我記錄了以下火焰圖,顯示呼叫CompareDepthAndScore()
的make_heap()
使用了該進程超過45%的CPU時間。
同時,還有一個未解決的、不相關的100% CPU使用率問題,發生在Bitcoin Core的偵錯模式建置中。這讓一些沒有執行偵錯模式建置,但注意到其節點上CPU使用率高的貢獻者和使用者感到困惑。雖然偵錯模式問題可能只影響了一些開發者,但另一個高CPU使用率問題影響了整個網路。例如,這包括AntPool和其他礦池,他們報告了其挖礦作業的問題,因為他們的節點無法及時處理收到的區塊。
觀察整個網路的ping時間可以揭示此阻斷服務的影響。由於Bitcoin Core的訊息處理是單執行緒的,因此一次只能建立或處理一條訊息,這意味著所有其他對等節點都必須等待。更長的等待時間會影響ping的回應時間。KIT DSN Bitcoin監控具有關於ICMP和Bitcoin協議ping的資料。比較這些資料可以讓我們確定節點軟體何時在訊息處理方面遇到問題。資料顯示,到主機的ICMP ping保持不受影響,但是,到Bitcoin節點軟體的中位數ping時間在4月底到5月初之間幾乎翻了一番,從大約25ms增加到超過50ms。5月8日,Bitcoin ping的中位數飆升至200ms,而ICMP ping保持不受影響。
還可以透過查看KIT DSN Bitcoin監控收集的區塊傳播延遲資料來看到影響。在2023年5月8日左右,可以看到區塊傳播延遲出現峰值。50%的可到達節點將區塊宣告給其監控節點所需的時間從不到一秒增加到超過五秒。同樣,90%的測量值從大約兩秒飆升到超過20秒。
不良的區塊傳播也會導致更多的過時區塊,因為礦池會在他們過時的區塊上挖掘更長的時間,而他們尚未看到的新區塊已經存在於網路中。根據我的stale-blocks資料集的資料,在5月3日(從過時區塊788016開始)到5月10日(以區塊789147結束)之間的一週內,觀察到十個過時區塊。這是大約每1000個區塊8.84個過時區塊的比率。相比之下,在區塊800000到900000之間(大約兩年),觀察到73個過時區塊。這是每1000個區塊0.73個過時區塊的比率。過時區塊率的這種10倍增長可能是由於區塊傳播受到顯著影響所致。
為什麼函數CTxMemPool::CompareDepthAndScore()
會使節點速度降低到難以處理P2P訊息的程度?在Bitcoin Core中,b-msghand
執行緒處理P2P訊息。例如,將新收到的區塊傳遞給驗證,回應ping,向其他對等節點宣告交易等等。
函數CTxMemPool::CompareDepthAndScore()
用於決定接下來要向對等節點宣告哪些交易。在Bitcoin P2P協議中,交易透過inv
(inventory)訊息宣告。Bitcoin Core向對等節點的交易宣告通常包含最多35個wtxid
條目。為了追蹤接下來要向對等節點宣告哪些交易,有一個每個對等節點的m_tx_inventory_to_send
集合。它包含節點認為對等節點尚未看到的交易。在為對等節點建構inventory訊息時,該集合會按交易依賴性和手續費率排序,以優先處理高手續費率交易,並避免洩漏節點瞭解交易的順序。為此,使用了CTxMemPool::CompareDepthAndScore()
比較函數。
在2023年5月初,大量與BRC-20代幣相關的交易被廣播。這意味著m_tx_inventory_to_send
集合的增長速度比平常更快,規模也比平常更大。結果,對集合進行排序花費了更多時間。在5月7日(UTC)晚上,VMPX BRC-20代幣的鑄造開始,這導致在6小時內廣播了超過30萬筆交易,此外還有其他正在進行的BRC-20代幣鑄造。這導致了5月8日觀察到的中位數ping和區塊傳播時間的峰值。
所謂的間諜節點只監聽inv
訊息,從不自行宣告交易,這加劇了影響。當對等節點向節點宣告交易時,節點可以將其從其m_tx_inventory_to_send
集合中移除,因為對等節點已知該交易,因此不再需要宣告。這意味著間諜節點的集合甚至更大,並且由於它們的耗盡速度較慢,因此需要花費更多時間進行排序。例如,間諜節點LinkingLion和其他節點很常見,並且通常與節點並行開啟多個連線。有時,我計算出的假定間諜節點比連線到我的節點的非間諜節點連線更多。
大量交易被廣播,加上間諜節點的放大作用,以及CTxMemPool::CompareDepthAndScore()
對大型m_tx_inventory_to_send
集合的非最佳排序,導致節點花費大量時間建立新的inventory訊息以進行交易中繼。由於訊息處理是單執行緒的,因此與其他對等節點的通訊顯著減慢。這達到了一個程度,即區塊無法及時處理,並且某些連線超時。
修復是雙重的。首先,所有要宣告的、已經被挖掘或由於其他原因不在記憶體池中的交易,都會在m_tx_inventory_to_send
集合排序之前被移除。以前,這些交易僅在集合排序後才被移除。這避免了花費時間對永遠不會被宣告的交易條目進行排序,並減少了要排序的集合的大小。其次,當m_tx_inventory_to_send
集合很大時,從集合中耗盡的條目數量會根據集合大小動態增加。這意味著當廣播大量交易時,節點將向其對等節點宣告更多交易,直到集合再次變小。該修復已及時回溯移植到2023年5月底的v25.0版本中。
雖然一組常規貢獻者知道正在發生這種情況,但此問題並未公開傳達給公眾。同時討論的偵錯模式的100% CPU使用率問題引起了混淆,甚至在常規Bitcoin Core貢獻者中也是如此。當時,我感覺這可以並且也許應該悄悄地修復,並且暫時不需要大量的宣傳。事後看來,也許更加公開和透明地處理這個問題也可以。大量的BRC-20廣播只持續了大約一週(但這事先並不知道),並且重新啟動節點會有助於一段時間。為了減輕該問題,例如,對於無法立即升級到具有修復版本的礦池(由於使用自訂修補程式執行),準備了一個間諜節點的封鎖清單,但我不知道它是否曾經被使用過。
雖然沒有針對此事件的專用通訊管道,但使用了一個未列出的與P2P貢獻者的IRC頻道,並且透過直接訊息邀請或告知感興趣的貢獻者有關這些事件。據我所知,沒有事件回應頻道,我也不知道鑑於Bitcoin開發的臨時性和分散性,一個事件回應頻道是否有幫助。沒有貢獻者負責事件回應,但每個人都可以提供幫助。
就我個人而言,我很高興我的監控證明對此有用。雖然我當時沒有設定丟棄連線的警報,並且只是透過查看儀表板才注意到它,但擁有它很有幫助。為了查明問題,擁有幾個可以隨意使用和執行的節點(例如,在上面執行perf top
)很有幫助。未來的監控應包括ping時間和丟棄連線的警報。