[インデックス 16698] ファイルの概要
このコミットは、GoランタイムにおけるNetBSD/ARMアーキテクチャ向けのシグナルハンドリングに関するバグ修正です。具体的には、runtime.sigreturn_tramp
関数がm->tls[0]
を使用してucontext
ポインタを保存する方法が再入可能(re-entry safe)ではなかった問題と、非Goスレッドでシグナルが受信された際にucontext
が適切に設定されない問題に対処しています。これにより、misc/cgo/test
が特定の条件下でハングアップする問題(Go issue 5337に関連)が解決されました。
コミット
commit e55517259217b29383d1a77e3c70e5cd19fa778b
Author: Shenghou Ma <minux.ma@gmail.com>
Date: Wed Jul 3 00:33:38 2013 +0800
runtime: fix runtime.sigreturn_tramp for NetBSD/ARM
using m->tls[0] to save ucontext pointer is not re-entry safe, and
the old code didn't set it before the early return when signal is
received on non-Go threads.
so misc/cgo/test used to hang when testing issue 5337.
R=golang-dev, bradfitz, rsc
CC=golang-dev
https://golang.org/cl/10076045
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/e55517259217b29383d1a77e3c70e5cd19fa778b
元コミット内容
runtime: fix runtime.sigreturn_tramp for NetBSD/ARM
using m->tls[0] to save ucontext pointer is not re-entry safe, and
the old code didn't set it before the early return when signal is
received on non-Go threads.
so misc/cgo/test used to hang when testing issue 5337.
変更の背景
この変更は、GoランタイムがNetBSD/ARM環境でシグナルを処理する際の既存のバグを修正するために行われました。特に、runtime.sigreturn_tramp
関数がucontext
ポインタを保存するためにm->tls[0]
(スレッドローカルストレージ)を使用していた方法に問題がありました。
問題の核心は以下の2点です。
- 再入可能性(Re-entry Safety)の欠如:
m->tls[0]
にucontext
ポインタを保存する方法は、シグナルハンドラが再入された場合に安全ではありませんでした。シグナルハンドラが実行中に別のシグナルが到着し、同じハンドラが再度呼び出される(再入する)と、m->tls[0]
に保存されたucontext
ポインタが上書きされ、元のコンテキストへの復元が正しく行えなくなる可能性がありました。 - 非Goスレッドでの
ucontext
設定の不備: Goランタイムが管理していないCGOなどによって作成された非Goスレッドでシグナルが受信された場合、古いコードでは早期リターン(early return)の前にucontext
がm->tls[0]
に適切に設定されていませんでした。これにより、シグナルハンドラからの復帰時に不正な状態になる可能性がありました。
これらの問題が複合的に作用し、misc/cgo/test
内のテストがハングアップするという具体的な症状として現れていました。このハングアップは、Go issue 5337に関連するテストケースで特に顕著でした。Go issue 5337は、Goランタイムが外部スレッド(CGOなど)で受信したSIGPROF
シグナルを適切に処理できるかどうかに焦点を当てた問題であり、このコミットはその問題の解決に貢献しています。
前提知識の解説
このコミットを理解するためには、以下の概念について理解しておく必要があります。
- シグナル (Signal): オペレーティングシステムがプロセスに送信する非同期通知です。プログラムの異常終了、ユーザーからの割り込み、タイマーの満了など、様々なイベントをプロセスに伝えるために使用されます。シグナルを受信したプロセスは、デフォルトの動作を実行するか、登録されたシグナルハンドラを実行します。
ucontext
(User Context): POSIXシステムにおけるユーザーコンテキストを表すデータ構造です。これには、CPUレジスタの状態、シグナルマスク、スタックポインタなど、プログラムの実行状態を完全に復元するために必要な情報が含まれています。シグナルハンドラから元の実行フローに戻る際や、コルーチン、スレッドの切り替えなどで使用されます。sigreturn_tramp
(Signal Return Trampoline): シグナルハンドラから元の実行コンテキストに復帰するためのアセンブリコードの小さな断片(トランポリン)です。シグナルハンドラが終了すると、通常はこのトランポリンが呼び出され、ucontext
構造体を使用してプロセスの状態をシグナル受信前の状態に復元し、元の実行を再開します。sigtramp
(Signal Trampoline): シグナルが到着した際に、カーネルからシグナルハンドラに制御を移す前に実行されるアセンブリコードの小さな断片です。このコードは、ucontext
などの情報をスタックに保存したり、シグナルハンドラに渡す引数を設定したりする役割を担います。m->tls[0]
(Thread Local Storage): Goランタイムにおけるスレッドローカルストレージ(TLS)の一種です。m
はGoランタイムのM(Machine)構造体を表し、これはOSスレッドに対応します。tls
はスレッドごとに固有のデータを保存するための領域であり、tls[0]
はその配列の最初の要素を指します。Goランタイムは、特定のOSスレッドに関連する情報をここに保存することがあります。- NetBSD/ARM: NetBSDはオープンソースのUnix系オペレーティングシステムであり、ARMはAdvanced RISC Machinesの略で、モバイルデバイスや組み込みシステムで広く使用されているCPUアーキテクチャです。このコミットは、NetBSD上でARMプロセッサを使用する環境に特化した修正です。
- CGO: Go言語の機能の一つで、C言語のコードをGoプログラムから呼び出すことを可能にします。CGOを使用すると、Goランタイムが直接管理しないOSスレッドが生成されることがあり、これらのスレッドでのシグナルハンドリングはGoランタイムにとって特別な考慮が必要になります。
- Go issue 5337: GoランタイムがCGOによって生成された外部スレッドで
SIGPROF
シグナル(プロファイリングなどで使用されるタイマーシグナル)を受信した際に、適切に処理できない問題に関するGoのバグトラッカー上の課題です。この問題は、Goランタイムが非Goスレッドのコンテキストを正しく保存・復元できないことに起因していました。
技術的詳細
このコミットの技術的な詳細は、NetBSD/ARMにおけるシグナルハンドリングのメカニズムと、Goランタイムがそれをどのように利用していたか、そしてその問題点を修正する方法にあります。
旧来の実装の問題点:
m->tls[0]
の不適切な使用: 以前のruntime.sigtramp
関数では、シグナルハンドラが呼び出される前に、受信したucontext
ポインタをm->tls[0]
に保存していました。そして、runtime.sigreturn_tramp
では、このm->tls[0]
からucontext
ポインタをロードしてsys_setcontext
システムコールを呼び出し、元のコンテキストに復帰しようとしていました。- 再入可能性の問題: シグナルハンドラが実行中に別のシグナルが到着し、同じシグナルハンドラが再入された場合、
m->tls[0]
に保存された最初のucontext
ポインタが、2番目のシグナルハンドラによって上書きされてしまいます。これにより、最初のシグナルハンドラが終了してsigreturn_tramp
が呼び出された際に、誤ったucontext
ポインタをロードしてしまい、プログラムがクラッシュしたり、予期せぬ動作をしたりする可能性がありました。 - 非Goスレッドの問題: CGOなどによって生成された非Goスレッドでシグナルが受信された場合、GoランタイムのM(Machine)構造体やTLSが適切に初期化されていない、またはGoランタイムの管理下にない場合があります。このような状況で
m->tls[0]
にucontext
ポインタを保存しようとすると、不正なメモリアクセスが発生したり、そもそもm
が有効な状態でないためにucontext
が保存されなかったりする問題がありました。特に、シグナルが非Goスレッドで受信され、かつ早期リターンパスが実行される場合、m->tls[0]
が設定されないままsigreturn_tramp
が呼び出される可能性があり、結果としてハングアップやクラッシュを引き起こしていました。
- 再入可能性の問題: シグナルハンドラが実行中に別のシグナルが到着し、同じシグナルハンドラが再入された場合、
新しい実装の解決策:
このコミットでは、ucontext
ポインタをm->tls[0]
に保存するという危険なアプローチを完全に廃止しました。代わりに、sigreturn_tramp
が呼び出された時点で、ucontext
構造体がスタック上に配置されているというNetBSDのシグナルハンドリング規約を利用します。
sigreturn_tramp
の変更:- 以前は
MOVW m_tls(m), R0
でm->tls[0]
からucontext
ポインタをロードしていました。 - 新しいコードでは、
ADD $0x80, R13, R0
という命令を使用しています。R13
はARMアーキテクチャにおけるスタックポインタ(SP)レジスタです。- シグナルが到着すると、カーネルはシグナル情報(
siginfo_t
)とucontext_t
構造体を現在のスタックフレームにプッシュします。 sigreturn_tramp
が呼び出された時点では、SP
(R13
)はsiginfo
構造体の先頭を指しています。- NetBSD/ARMでは、
ucontext_t
構造体はsiginfo_t
構造体の直後に配置され、そのサイズは0x80
バイト(128バイト)です。 - したがって、
SP
に0x80
を加算することで、スタック上のucontext
構造体の先頭アドレスを正確に計算し、それをR0
レジスタ(システムコール呼び出し規約で最初の引数に使用される)にロードします。
- これにより、
ucontext
ポインタをスレッドローカルストレージに依存することなく、直接スタックから取得できるようになり、再入可能性の問題と非Goスレッドでの初期化不足の問題が同時に解決されます。
- 以前は
sigtramp
の変更:- 以前は
MOVW R2, m_tls(m)
という命令でucontext
ポインタ(R2
に格納されている)をm->tls[0]
に保存していました。 - このコミットでは、この行を完全に削除しています。これにより、
ucontext
ポインタをTLSに保存するという問題のある動作がなくなります。
- 以前は
この修正により、GoランタイムはNetBSD/ARM環境でより堅牢なシグナルハンドリングを実現し、特にCGOを使用するアプリケーションにおける安定性が向上しました。
コアとなるコードの変更箇所
--- a/src/pkg/runtime/sys_netbsd_arm.s
+++ b/src/pkg/runtime/sys_netbsd_arm.s
@@ -174,9 +174,9 @@ TEXT runtime·sigprocmask(SB),7,$0
RET
TEXT runtime·sigreturn_tramp(SB),7,$-4
-\t// in runtime·sigtramp, we saved ucontext into m->tls[0],
-\t// here we just load it and call sys_setcontext
-\tMOVW m_tls(m), R0
+\t// on entry, SP points to siginfo, we add sizeof(ucontext)
+\t// to SP to get a pointer to ucontext.
+\tADD $0x80, R13, R0 // 0x80 == sizeof(UcontextT)
SWI $0xa00134 // sys_setcontext
// something failed, we have to exit
MOVW $0x4242, R0 // magic return number
@@ -223,9 +223,6 @@ TEXT runtime·sigtramp(SB),7,$24
MOVW R1, 8(R13) // info
MOVW R2, 12(R13) // context
MOVW R4, 16(R13) // gp
-\t// we also save the ucontext into m->tls[0] for easy
-\t// signal return
-\tMOVW R2, m_tls(m)
BL runtime·sighandler(SB)
コアとなるコードの解説
このコミットは、src/pkg/runtime/sys_netbsd_arm.s
ファイル内の2つのアセンブリ関数、runtime·sigreturn_tramp
とruntime·sigtramp
に対する変更を含んでいます。
TEXT runtime·sigreturn_tramp(SB),7,$-4
の変更
-
変更前:
// in runtime·sigtramp, we saved ucontext into m->tls[0], // here we just load it and call sys_setcontext MOVW m_tls(m), R0
この部分では、以前に
runtime·sigtramp
でm->tls[0]
に保存されたucontext
ポインタをR0
レジスタにロードしていました。m_tls(m)
は、現在のM(Machine)構造体に関連付けられたスレッドローカルストレージのベースアドレスを指し、そこからucontext
ポインタを取得していました。 -
変更後:
// on entry, SP points to siginfo, we add sizeof(ucontext) // to SP to get a pointer to ucontext. ADD $0x80, R13, R0 // 0x80 == sizeof(UcontextT)
この変更がこのコミットの核心です。
R13
はARMアーキテクチャにおけるスタックポインタ(SP)レジスタです。- NetBSDのシグナルハンドリング規約では、シグナルハンドラが呼び出される際、カーネルはスタックにシグナル情報(
siginfo_t
)とユーザーコンテキスト(ucontext_t
)をプッシュします。sigreturn_tramp
が実行される時点では、R13
(SP)はsiginfo_t
構造体の先頭を指しています。 ucontext_t
構造体はsiginfo_t
の直後に配置されており、そのサイズは0x80
バイト(128バイト)です。- したがって、
ADD $0x80, R13, R0
という命令は、現在のスタックポインタR13
に0x80
を加算することで、スタック上のucontext_t
構造体の先頭アドレスを計算し、その結果をR0
レジスタに格納します。 R0
は通常、システムコールや関数呼び出しの最初の引数を渡すために使用されるレジスタであるため、このucontext
ポインタは後続のSWI $0xa00134
(sys_setcontext
システムコール)に渡されます。- この変更により、
ucontext
ポインタをm->tls[0]
に依存することなく、直接スタックから取得できるようになり、再入可能性の問題と非Goスレッドでの初期化不足の問題が解決されます。
TEXT runtime·sigtramp(SB),7,$24
の変更
-
変更前:
// we also save the ucontext into m->tls[0] for easy // signal return MOVW R2, m_tls(m)
この行は、
runtime·sigtramp
がucontext
ポインタ(R2
レジスタに格納されている)をm->tls[0]
に保存していた部分です。この保存は、sigreturn_tramp
がucontext
を簡単に取得できるようにするためのものでしたが、前述の通り再入可能性と非Goスレッドの問題を引き起こしていました。 -
変更後: この行は完全に削除されています。これにより、
ucontext
ポインタをm->tls[0]
に保存するという問題のある動作がなくなります。sigreturn_tramp
がスタックから直接ucontext
を取得するようになったため、この保存は不要になりました。
これらの変更により、NetBSD/ARM環境におけるGoランタイムのシグナルハンドリングがより堅牢になり、特にCGOを使用する際の安定性が向上しました。
関連リンク
- Go CL 10076045: https://golang.org/cl/10076045
- Go issue 5337:
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQExuzDFgLKy3IhPOOqg_7iuqady9xLXULFyuCYEytUi0BpazCK3G_W80xyteE8NWML1760hHqwlBM7b5bU5nbMpxD_k5K1jqtuXuAKlJNo90A_N2PJZ4s0f3PJyzBKfzZk5xQKv1ZCJ9uArwsW35r2WVggNLEVeE6ikLyynAaa-snxb0lpdOWV-cEOBBtdZC8DqCWl7ddvK2U6f__gXPAl9rk4eVWx0Uh7K8K0nXw==
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHl-Lt34hSGSfpHKP6ghlC-gY8kMakFW04sNpwbdvX-vb7rcy8iaTbRBtfeNN4b8q7iW5mEoDoF74vcR4f0bNCc0tYyZLyHd1WnC7TTaYEpqf8IuPtKBL1xo16IYBkIfoUC096PFdtH8XO50msTtZnqPPUWHfmwm36iPhKrMKnJr5LxLol-jUO7Ls7Jx2hYnfHliSyd3Q7mZYfi81-
参考にした情報源リンク
- Go issue 5337に関するWeb検索結果
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQExuzDFgLKy3IhPOOqg_7iuqady9xLXULFyuCYEytUi0BpazCK3G_W80xyteE8NWML1760hHqwlBM7b5bU5nbMpxD_k5K1jqtuXuAKlJNo90A_N2PJZ4s0f3PJyzBKfzZk5xQKv1ZCJ9uArwsW35r2WVggNLEVeE6ikLyynAaa-snxb0lpdOWV-cEOBBtdZC8DqCWl7ddvK2U6f__gXPAl9rk4eVWx0Uh7K8K0nXw==
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHl-Lt34hSGSfpHKP6ghlC-gY8kMakFW04sNpwbdvX-vb7rcy8iaTbRBtfeNN4b8q7iW5mEoDoF74vcR4f0bNCc0tYyZLyHd1WnC7TTaYEpqf8IuPtKBL1xo16IYBkIfoUC096PFdtH8XO50msTtZnqPPUWHfmwm36iPhKrMKnJr5LxLol-jUO7Ls7Jx2hYnfHliSyd3Q7mZYfi81-