ニュース

32ビット組み込みシステムにおけるGoランタイムバグ:netpollエイリアシングの深掘り

32ビットARMシステムでGoのnetpollに散発的なクラッシュが発生。ランタイムのポインタエイリアシングバグが原因で、数年にわたる報告の後に修正されました。

August 29, 2026· 1 min read· 出典: sigma star gmbh
32ビット組み込みシステムにおけるGoランタイムバグ:netpollエイリアシングの深掘り

32ビットARM組み込みLinuxシステムでGoアプリケーションがクラッシュする問題は、アプリケーションのバグではなくランタイムのバグであることが判明しました。致命的なエラーruntime: netpoll: eventfd ready for something unexpectedは、Goのnetpollメカニズムにおける内部の前提条件を示していました。

根本原因:32ビットリトルエンディアンシステムでは、Goのタグ付きポインタロジックが32ビットアドレスとタグビットを8バイトのワードにパックします。epollイベントのev.Dataフィールドには、netpollEventFdへの生のポインタまたはpollDescへのタグ付きポインタが格納されます。ev.Dataをイベントfdのアドレスと比較する際、コードはuintptrにキャストしますが、32ビットでは4バイトしかないため、8バイトフィールドの下半分しかチェックしません。

このエイリアシングにより、fdseqタグ(再利用されたpollDescオブジェクトのカウンタ)が数百万に達すると、netpollEventFdのアドレスと一致する可能性があります。多くのpollDescオブジェクトを作成する長時間実行プログラムは、最終的にこれに遭遇し、netpollがソケットfdをイベントfdと誤認します。

このバグはGo 1.14(2020年)で導入され、2025年3月まで気付かれず、32ビットARMおよびi386システムからの報告のみがありました。2026年にマージされた修正では、イベントfdに対して生のポインタの代わりにタグ付きnil pollDescを格納し、エイリアシングを排除しました。

この話は、あまり一般的でないアーキテクチャでのテストの重要性を浮き彫りにしています。Goチーム自身の32ビットテスト不足が、このバグを何年も存続させた可能性があります。

Goランタイムは同じフィールドに生ポインタとタグ付きポインタの両方を格納し、32ビットリトルエンディアンシステムでは生ポインタがfdseqタグとエイリアスし、netpollがソケットfdをイベントfdと誤認します。
Manul X 編集部
32ビットリトルエンディアンシステムにおけるev.Dataのポインタレイアウト
比較
ケース下位4バイト上位4バイト比較結果
タグ付きpollDescfdseq(例:0x00000012)*pollDesc(例:0x00123456)下位バイトがfdseqと一致し、イベントfdとは一致しない
生のイベントfdポインタ&netpollEventFd(例:0x00123456)0(未使用)下位バイトがイベントfdアドレスと一致