[インデックス 13365] ファイルの概要
このコミットは、GoランタイムにおけるLinux/ARMアーキテクチャでのSigaction
構造体の定義と、rt_sigaction
システムコールの使用方法に関するバグを修正するものです。具体的には、Sigaction
構造体内のsa_mask
フィールドのサイズが誤っていた点と、rt_sigaction
システムコールの最後の引数に固定値8
が渡されていた点を修正し、より堅牢なシグナルハンドリングを実現しています。また、Linux/386およびLinux/amd64アーキテクチャの関連ファイルも同様の修正が適用され、rt_sigaction
の戻り値のチェックも追加されています。
コミット
commit d4c4f4d2c4cdfc9e713383d419b528726cfbbb20
Author: Shenghou Ma <minux.ma@gmail.com>
Date: Wed Jun 20 01:17:03 2012 +0800
runtime: fix struct Sigaction for Linux/ARM
if we were to use sizeof(sa.sa_mask) instead of 8 as the last argument
to rt_sigaction, we would have already fixed this bug, so also updated
Linux/386 and Linux/amd64 files to use that; also test the return value
of rt_sigaction.
R=dave, rsc
CC=golang-dev
https://golang.org/cl/6297087
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/d4c4f4d2c4cdfc9e713383d419b528726cfbbb20
元コミット内容
runtime: fix struct Sigaction for Linux/ARM
if we were to use sizeof(sa.sa_mask) instead of 8 as the last argument
to rt_sigaction, we would have already fixed this bug, so also updated
Linux/386 and Linux/amd64 files to use that; also test the return value
of rt_sigaction.
R=dave, rsc
CC=golang-dev
https://golang.org/cl/6297087
変更の背景
この変更の背景には、Linuxシステムコールであるrt_sigaction
の正しい使用方法と、異なるアーキテクチャ間でのシグナルマスクのサイズの違いがあります。
rt_sigaction
システムコールは、特定のシグナルを受信した際のプロセスの動作を変更するために使用されます。このシステムコールは4つの引数を取りますが、その最後の引数sigsetsize
は、act
およびoldact
構造体内のシグナルセット(sa_mask
)のサイズをバイト単位で指定する必要があります。この値は、現在のところsizeof(sigset_t)
である必要があります。
元のコードでは、Linux/ARMアーキテクチャにおいてSigaction
構造体のsa_mask
フィールドがuint32
(4バイト)として定義されていました。しかし、Linuxのsigset_t
型は通常64ビット(8バイト)のサイズを持つことが多く、特にARMアーキテクチャにおいても同様です。この不一致により、rt_sigaction
システムコールに渡されるsa_mask
のサイズが正しくなく、シグナルハンドリングが意図した通りに機能しない可能性がありました。
さらに、rt_sigaction
システムコールの最後の引数に、マジックナンバーである固定値8
が直接渡されていました。これは、sigset_t
のサイズが8バイトであることを前提としたものでしたが、この固定値を使用する代わりにsizeof(sa.sa_mask)
を使用していれば、sa_mask
の定義が誤っていたとしても、このバグは早期に発見され、修正されていた可能性がありました。
このコミットは、これらの問題を解決し、GoランタイムがLinuxシステムコールをより正確かつ堅牢に利用できるようにすることを目的としています。また、Linux/386およびLinux/amd64アーキテクチャの関連ファイルにも同様の修正を適用することで、コードの一貫性と移植性を向上させています。
前提知識の解説
1. シグナル (Signal)
シグナルは、Unix系OSにおいてプロセス間で非同期にイベントを通知するためのメカニズムです。例えば、Ctrl+Cを押すとSIGINT
シグナルがプロセスに送られ、プロセスを終了させることができます。シグナルには様々な種類があり、それぞれ異なるイベントに対応しています。
2. sigaction
と rt_sigaction
システムコール
sigaction
: 古いシグナルハンドリングのシステムコールです。シグナルに対するアクション(シグナルを無視する、デフォルトの動作を行う、カスタムのハンドラ関数を実行する)を設定するために使用されます。rt_sigaction
:sigaction
の改良版で、リアルタイムシグナルや拡張されたsigset_t
型をサポートするために導入されました。Goランタイムのような低レベルのシステムプログラミングでは、通常こちらのシステムコールが使用されます。
rt_sigaction
システムコールのプロトタイプは以下のようになります(簡略化):
int rt_sigaction(int signum, const struct sigaction *act, struct sigaction *oldact, size_t sigsetsize);
signum
: 処理するシグナル番号。act
: 新しいシグナルアクションを指定するstruct sigaction
へのポインタ。oldact
: 以前のシグナルアクションを保存するstruct sigaction
へのポインタ(不要な場合はNULL
)。sigsetsize
:act
およびoldact
構造体内のシグナルセット(sa_mask
)のサイズをバイト単位で指定します。この値は、sizeof(sigset_t)
である必要があります。
3. struct sigaction
struct sigaction
は、シグナルハンドリングの詳細を設定するための構造体です。主要なフィールドは以下の通りです。
sa_handler
またはsa_sigaction
: シグナルハンドラ関数へのポインタ。sa_mask
: シグナルハンドラが実行されている間にブロックされるシグナルのセットを指定するsigset_t
型の変数。sa_flags
: シグナルハンドリングの動作を変更するためのフラグ。
4. sigset_t
と sa_mask
sigset_t
: シグナルのセットを表すデータ型です。通常、ビットマスクとして実装され、各ビットが特定のシグナルに対応します。sa_mask
:struct sigaction
内のフィールドで、シグナルハンドラが実行されている間に追加でブロックされるシグナルを指定します。これにより、シグナルハンドラが実行中に他のシグナルによって中断されるのを防ぐことができます。sa_mask
のサイズは、システムによって定義されるsigset_t
のサイズと一致している必要があります。Linuxでは、多くのアーキテクチャでsigset_t
は64ビット(8バイト)です。
5. Goランタイムにおけるシグナルハンドリング
Goプログラムは、os/signal
パッケージを通じてOSシグナルと対話できます。しかし、Goランタイム自体は、ガベージコレクション、プロファイリング、パニック処理などの内部的な目的のために、低レベルでシグナルを処理します。この低レベルのシグナルハンドリングは、C言語で書かれたGoランタイムのコード(src/pkg/runtime
ディレクトリ以下)で行われ、OSのシステムコールを直接呼び出します。
6. Linux/ARMアーキテクチャの特性
Linuxは様々なCPUアーキテクチャをサポートしており、ARMもその一つです。一般的に、シグナルハンドリングの概念はPOSIX標準に準拠しているため、アーキテクチャ間で大きな違いはありません。しかし、シグナルの数値や、低レベルのカーネル実装の詳細、データ型のサイズ(特にsigset_t
のようなシステム依存の型)は、アーキテクチャによって異なる場合があります。このコミットでは、Linux/ARMにおけるsigset_t
のサイズが他のアーキテクチャ(386, amd64)と異なる可能性、またはその定義が不正確であったことが問題の根源となっています。
技術的詳細
このコミットの技術的な核心は、rt_sigaction
システムコールの第4引数であるsigsetsize
の正確な値の指定と、Sigaction
構造体におけるsa_mask
フィールドの適切なデータ型です。
-
rt_sigaction
のsigsetsize
引数:rt_sigaction
システムコールは、シグナルハンドラを設定する際に、シグナルマスクのサイズを明示的にカーネルに伝える必要があります。このサイズは、sigset_t
型の実際のサイズと厳密に一致していなければなりません。もしこの値が誤っていると、カーネルはシグナルマスクを正しく解釈できず、シグナルハンドリングが予期せぬ動作をする可能性があります。例えば、シグナルが正しくブロックされなかったり、メモリ破壊が発生したりする恐れがあります。 元のコードでは、この引数に固定値8
が渡されていました。これは、多くのシステムでsigset_t
が8バイトであるという一般的な知識に基づいていたと考えられます。しかし、これはハードコーディングであり、将来的なアーキテクチャの変更や、特定の環境でのsigset_t
の異なるサイズに対応できません。 -
Sigaction
構造体のsa_mask
フィールドの型: Linux/ARMのsrc/pkg/runtime/defs_linux_arm.h
ファイルでは、Sigaction
構造体のsa_mask
フィールドがuint32
(4バイト)として定義されていました。しかし、Linuxのsigset_t
は通常64ビット(8バイト)です。この不一致が、rt_sigaction
システムコールに渡されるsa_mask
のサイズが正しくないという問題を引き起こしていました。 このコミットでは、sa_mask
の型をuint32
からuint64
に変更することで、Sigaction
構造体内のsa_mask
がsigset_t
の実際のサイズと一致するように修正しています。 -
sizeof(sa.sa_mask)
の使用: このコミットの重要な改善点は、rt_sigaction
システムコールの第4引数に固定値8
を渡す代わりに、sizeof(sa.sa_mask)
を使用するように変更したことです。sizeof
演算子を使用することで、コンパイル時にsa.sa_mask
の実際のサイズが計算され、その値がシステムコールに渡されます。- これにより、
sa_mask
の型定義が変更された場合でも、rt_sigaction
に渡されるサイズが自動的に更新されるため、将来的な互換性が向上し、同様のバグが再発するのを防ぐことができます。 - コミットメッセージにあるように、「もし
sizeof(sa.sa_mask)
を最後の引数として使用していれば、このバグは既に修正されていたはずだ」という記述は、この変更の重要性を示しています。
-
rt_sigaction
の戻り値のチェック: システムコールは失敗する可能性があります。rt_sigaction
も例外ではありません。このコミットでは、rt_sigaction
の呼び出しが成功したかどうかをチェックし、失敗した場合にはruntime·throw("rt_sigaction failure");
を呼び出してランタイムパニックを発生させるように変更しています。これにより、シグナルハンドリングの設定に問題が発生した場合に、Goプログラムがより早期に異常を検知し、診断できるようになります。
これらの変更は、GoランタイムがLinuxシステムとより正確に連携し、シグナルハンドリングの堅牢性と信頼性を向上させる上で不可欠です。特に、異なるアーキテクチャ間での移植性を考慮する上で、データ型のサイズに関する正確な知識と、それに基づいたコードの実装が重要であることを示しています。
コアとなるコードの変更箇所
このコミットでは、以下の5つのファイルが変更されています。
-
src/pkg/runtime/defs_linux_arm.h
:struct Sigaction
内のsa_mask
フィールドの型がuint32
からuint64
に変更されました。
-
src/pkg/runtime/os_linux.h
:runtime·rt_sigaction
関数の宣言が、戻り値の型をvoid
からint32
に変更されました。これは、rt_sigaction
システムコールが成功/失敗を示す整数値を返すためです。
-
src/pkg/runtime/signal_linux_386.c
:runtime·setsig
関数内でruntime·rt_sigaction
を呼び出す際、最後の引数が固定値8
からsizeof(sa.sa_mask)
に変更されました。runtime·rt_sigaction
の戻り値がチェックされ、0
でない場合はruntime·throw("rt_sigaction failure");
が呼び出されるようになりました。
-
src/pkg/runtime/signal_linux_amd64.c
:runtime·setsig
関数内でruntime·rt_sigaction
を呼び出す際、最後の引数が固定値8
からsizeof(sa.sa_mask)
に変更されました。runtime·rt_sigaction
の戻り値がチェックされ、0
でない場合はruntime·throw("rt_sigaction failure");
が呼び出されるようになりました。
-
src/pkg/runtime/signal_linux_arm.c
:runtime·setsig
関数内でruntime·rt_sigaction
を呼び出す際、最後の引数が固定値8
からsizeof(sa.sa_mask)
に変更されました。runtime·rt_sigaction
の戻り値がチェックされ、0
でない場合はruntime·throw("rt_sigaction failure");
が呼び出されるようになりました。
コアとなるコードの解説
src/pkg/runtime/defs_linux_arm.h
の変更
--- a/src/pkg/runtime/defs_linux_arm.h
+++ b/src/pkg/runtime/defs_linux_arm.h
@@ -143,6 +143,6 @@ struct Sigaction {
void *sa_handler;
uint32 sa_flags;
void *sa_restorer;
- uint32 sa_mask;
+ uint64 sa_mask;
};
#pragma pack off
この変更は、Linux/ARMアーキテクチャにおけるSigaction
構造体のsa_mask
フィールドのデータ型をuint32
(32ビット符号なし整数)からuint64
(64ビット符号なし整数)に修正しています。これは、Linuxシステムコールが期待するsigset_t
のサイズが通常64ビットであるため、Goランタイムの内部定義をこれに合わせることで、シグナルマスクが正しく扱われるようにするための重要な修正です。
src/pkg/runtime/os_linux.h
の変更
--- a/src/pkg/runtime/os_linux.h
+++ b/src/pkg/runtime/os_linux.h
@@ -10,7 +10,7 @@ int32 runtime·futex(uint32*, int32, uint32, Timespec*, uint32*, uint32);
int32 runtime·clone(int32, void*, M*, G*, void(*)(void));
struct Sigaction;
-void runtime·rt_sigaction(uintptr, struct Sigaction*, void*, uintptr);
+int32 runtime·rt_sigaction(uintptr, struct Sigaction*, void*, uintptr);
void runtime·setsig(int32, void(*)(int32, Siginfo*, void*, G*), bool);
void runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp);
runtime·rt_sigaction
関数の宣言がvoid
からint32
に戻り値の型を変更しています。これは、rt_sigaction
システムコールが成功時には0
を返し、失敗時にはエラーコードを返すため、その戻り値をGoランタイムが適切に処理できるようにするための変更です。
src/pkg/runtime/signal_linux_386.c
, src/pkg/runtime/signal_linux_amd64.c
, src/pkg/runtime/signal_linux_arm.c
の変更
これら3つのファイルにおける変更は同様のパターンに従っています。以下にsignal_linux_386.c
の例を示します。
--- a/src/pkg/runtime/signal_linux_386.c
+++ b/src/pkg/runtime/signal_linux_386.c
@@ -129,7 +129,8 @@ runtime·setsig(int32 i, void (*fn)(int32, Siginfo*, void*, G*), bool restart)
if(fn == runtime·sighandler)
fn = (void*)runtime·sigtramp;
sa.k_sa_handler = fn;
- runtime·rt_sigaction(i, &sa, nil, 8);
+ if(runtime·rt_sigaction(i, &sa, nil, sizeof(sa.sa_mask)) != 0)
+ runtime·throw("rt_sigaction failure");
}
このコードスニペットは、runtime·setsig
関数内でruntime·rt_sigaction
システムコールを呼び出す部分の変更を示しています。
-
sizeof(sa.sa_mask)
への変更: 以前はruntime·rt_sigaction(i, &sa, nil, 8);
のように、最後の引数に固定値8
が渡されていました。これは、sigset_t
のサイズが8バイトであることを前提としたマジックナンバーです。 変更後、この引数はsizeof(sa.sa_mask)
となりました。これにより、sa.sa_mask
フィールドの実際のサイズ(このコミットでuint64
に変更されたため8バイト)が動的に計算され、システムコールに渡されます。これにより、コードの堅牢性が向上し、sa_mask
の型定義が将来変更された場合でも、この部分のコードを修正する必要がなくなります。 -
戻り値のチェックとエラーハンドリング:
if(runtime·rt_sigaction(i, &sa, nil, sizeof(sa.sa_mask)) != 0)
という条件が追加されました。これは、rt_sigaction
システムコールの戻り値をチェックしています。システムコールが成功すると0
を返すため、0
以外の値が返された場合はエラーが発生したことを意味します。 エラーが発生した場合、runtime·throw("rt_sigaction failure");
が呼び出され、Goランタイムがパニックを起こしてプログラムを終了させます。これにより、シグナルハンドリングの設定に問題がある場合に、Goプログラムが異常な状態のまま実行を続けることを防ぎ、問題の早期発見とデバッグを支援します。
これらの変更は、GoランタイムがLinuxシステムコールをより正確かつ安全に利用するための重要なステップであり、特にクロスアーキテクチャの互換性と堅牢性を高める上で不可欠です。
関連リンク
- Go CL 6297087: https://golang.org/cl/6297087
参考にした情報源リンク
rt_sigaction
system call arguments: https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFi2BoQte756_eBfO2b5urbUi4SjZoot9-yduTnbNlTW23RWm49_6IlcRD2w-ZknbyLbCt_8mV1KYQuxsK2IDQQZFZQ322jYg8-2it_dFxpYfYlus5xuNamgldXAFPfqTJwpdqrd9XxxGikmb1rafQgDglbcUxc5LI7ltUL0MOSZQMr1RA8nsc2cMHxI43-JEtc- Linux
Sigaction
structsa_mask
: https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGAkX-z4LFOdtZ0JUBBDz-JV0cNrGsmj4eG0swTJyjX_ZAb3AogJDhWbOHbOYNXXd5VMczQAdEepJDYfUBT1N4u1UtOGk-zJIU3GvDHmzRc7JtR-St4vm1jZavf9CNB35174a0-dZQV_mvyoN6YTQ42 - Go runtime signal handling: https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHlpYQ4wBm1oUlf9Uike5TDsC090N2kSOk2krlv09NDSqNrD-zfD-GhRJXWTKnFOFm0F7hM7BsoiovfCgUD67iBA5UwrGmdMoDReBk8G9jdAJNAd8UigZUM-GxCK0pMo9GqbfW7WPVIew==
- Linux ARM signal handling differences: https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGtB1LN96VPfxch1YRnX3mBaqGuf_Jh35EM5JB7bbqKwTLDFgyf4ng04u-hEkpjdufMk8HJ9qRf3qatct_cb-RGNolORYVwXxaUXjRKCXx-5Iv_CSOl3jP4LEXNl0JoIODNmuTMHRIvt3Y4ZJowbH-jLJU9hHXSg28TcaUF5ANWwx16bV84