[インデックス 15825] ファイルの概要
このコミットは、Go言語のランタイムにおけるNetBSDおよびOpenBSDプラットフォームでのsemasleep
関数のオーバーフロー保護に関する修正です。具体的には、tv_sec
(秒を表すタイムスタンプ)の型と、semasleep
が期待する引数の解釈の違いによって生じていた誤ったオーバーフローチェックを是正しています。
コミット
commit 51f14a9fe285cbc5579ae59fc7f72082c29ce266
Author: Rémy Oudompheng <oudomphe@phare.normalesup.org>
Date: Tue Mar 19 07:08:26 2013 +0100
runtime: fix erroneous overflow protection on netbsd/openbsd semasleep.
On NetBSD tv_sec is already an int64 so no need for a test.
On OpenBSD, semasleep expects a Unix time as argument,
and 1<<30 is in 2004.
R=golang-dev, iant
CC=golang-dev
https://golang.org/cl/7810044
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/51f14a9fe285cbc5579ae59fc7f72082c29ce266
元コミット内容
runtime: fix erroneous overflow protection on netbsd/openbsd semasleep.
On NetBSD tv_sec is already an int64 so no need for a test.
On OpenBSD, semasleep expects a Unix time as argument,
and 1<<30 is in 2004.
変更の背景
Go言語のランタイムは、異なるオペレーティングシステム(OS)やアーキテクチャに対応するために、OS固有のシステムコールやデータ構造を抽象化して利用しています。このコミットは、NetBSDとOpenBSDという2つのBSD系OSにおけるsemasleep
(セマフォを用いたスリープ処理)の実装に関する問題に対処しています。
問題の根源は、semasleep
関数に渡されるタイムアウト値の計算と、その値に対するオーバーフロー保護のロジックにありました。特に、以下の点が課題となっていました。
- NetBSDにおける
tv_sec
の型: NetBSDでは、タイムスタンプの秒部分を格納するtv_sec
フィールドが既にint64
型であるため、以前のコードで行われていたint64
へのキャストやオーバーフローチェックが不要でした。これは冗長であるだけでなく、潜在的に誤った挙動を引き起こす可能性がありました。 - OpenBSDにおける
semasleep
の引数解釈: OpenBSDのsemasleep
は、絶対的なUnix時間(エポックからの秒数)を期待していました。しかし、既存のオーバーフロー保護ロジックでは、相対的な時間(スリープ期間)を考慮した1LL<<30
という値(約10億秒、2004年頃に相当)で上限を設けていました。これは、将来の絶対時刻がこの値を超える場合に、不適切なタイムアウト値が設定される原因となっていました。
これらの問題により、semasleep
が意図した通りに動作しない、または予期せぬタイムアウトが発生する可能性がありました。このコミットは、これらのプラットフォーム固有の特性を正しく考慮し、オーバーフロー保護ロジックを修正することで、ランタイムの安定性と正確性を向上させることを目的としています。
前提知識の解説
このコミットを理解するためには、以下の概念について知っておく必要があります。
- Goランタイム (Go Runtime): Goプログラムの実行を管理する低レベルのコンポーネントです。ガベージコレクション、スケジューリング、システムコールインターフェースなど、多くの重要な機能を提供します。OS固有の処理は、ランタイム内のOS固有のファイル(例:
os_netbsd.c
,os_openbsd.c
)で実装されます。 semasleep
: セマフォ(Semaphore)は、並行プログラミングにおいて共有リソースへのアクセスを制御するための同期プリミティブです。semasleep
は、セマフォが利用可能になるまで、または指定されたタイムアウト期間が経過するまで、現在のスレッド(または軽量プロセス)をスリープさせる関数です。OSによって実装が異なります。Timespec
構造体: POSIX標準で定義されている時間値を表す構造体で、通常は秒 (tv_sec
) とナノ秒 (tv_nsec
) の2つのフィールドを持ちます。struct timespec { time_t tv_sec; /* seconds */ long tv_nsec; /* nanoseconds */ };
time_t
の具体的な型(int32
かint64
かなど)は、システムやコンパイラによって異なります。- Unix時間 (Epoch Time): 1970年1月1日00:00:00 UTC(協定世界時)からの経過秒数で時間を表すシステムです。多くのUNIX系システムで内部的に時間を扱う際に使用されます。
- オーバーフロー保護 (Overflow Protection): 数値計算において、変数が格納できる最大値を超えてしまうこと(オーバーフロー)を防ぐための仕組みです。オーバーフローが発生すると、予期せぬ値になったり、プログラムがクラッシュしたりする可能性があります。
- ビットシフト演算子 (
<<
):1LL << 30
のような表記は、ビットシフト演算子を使用しています。LL
はlong long
型であることを示し、1
を左に30ビットシフトするという意味です。これは2の30乗
、つまり1,073,741,824
という値を表します。同様に1LL << 31
は2の31乗
、つまり2,147,483,648
を表します。これらの値は、32ビット符号付き整数型の最大値(約21億)に近い値です。
技術的詳細
このコミットは、GoランタイムのNetBSDとOpenBSD固有のセマフォスリープ実装であるruntime·semasleep
関数内のタイムアウト計算ロジックを修正しています。
NetBSD (src/pkg/runtime/os_netbsd.c
) の変更点
NetBSDのruntime·semasleep
関数では、以前はns
(ナノ秒単位のタイムアウト値)を秒に変換し、その結果をsecs
というint64
型の変数に格納していました。その後、secs
が1LL<<30
(約10億秒)を超える場合に、secs
を1LL<<30
に制限するというオーバーフロー保護ロジックがありました。
// 変更前
int64 secs;
// ...
secs = ns/1000000000LL;
// Avoid overflow
if(secs > 1LL<<30)
secs = 1LL<<30;
ts.tv_sec = secs;
このコミットでは、以下の変更が行われました。
int64 secs;
の宣言が削除されました。ts.tv_sec = secs;
の行がts.tv_sec = ns/1000000000LL;
に直接変更されました。- それに伴い、
// Avoid overflow
とその下のif
文によるオーバーフローチェックが削除されました。
この変更の理由は、コミットメッセージにある通り「On NetBSD tv_sec is already an int64 so no need for a test.」です。NetBSDのTimespec
構造体におけるtv_sec
フィールドは既にint64
型であるため、ns/1000000000LL
の結果を直接ts.tv_sec
に代入してもオーバーフローの心配がなく、冗長な中間変数とオーバーフローチェックが不要になったためです。これにより、コードが簡潔になり、潜在的な誤解が排除されました。
OpenBSD (src/pkg/runtime/os_openbsd.c
) の変更点
OpenBSDのruntime·semasleep
関数でも、同様にns
を秒に変換し、secs
というint64
型の変数に格納していました。ここでもオーバーフロー保護ロジックが存在しましたが、その値が問題でした。
// 変更前
secs = ns/1000000000LL;
// Avoid overflow
if(secs > 1LL<<30)
secs = 1LL<<30;
ts.tv_sec = secs;
このコミットでは、OpenBSDのオーバーフロー保護ロジックが以下のように変更されました。
// 変更後
secs = ns/1000000000LL;
// Avoid overflow
if(secs >= 1LL<<31)
secs = (1LL<<31) - 1;
ts.tv_sec = secs;
変更点は以下の通りです。
- オーバーフローチェックの条件が
if(secs > 1LL<<30)
からif(secs >= 1LL<<31)
に変更されました。 - オーバーフロー時の制限値が
secs = 1LL<<30;
からsecs = (1LL<<31) - 1;
に変更されました。
この変更の理由は、コミットメッセージにある通り「On OpenBSD, semasleep expects a Unix time as argument, and 1<<30 is in 2004.」です。OpenBSDのsemasleep
は、相対的なタイムアウト期間ではなく、絶対的なUnix時間(エポックからの秒数)を期待します。1LL<<30
という値は、2004年頃のUnix時間に相当するため、それ以降の絶対時刻を表現できませんでした。
新しい制限値である1LL<<31 - 1
は、32ビット符号付き整数で表現できる最大値(約21億秒)に相当します。これにより、より将来の絶対時刻までを適切に表現できるようになり、semasleep
が期待する引数のセマンティクスに合致するようになりました。この修正により、OpenBSD上でのGoランタイムのタイムアウト処理がより正確かつ堅牢になります。
コアとなるコードの変更箇所
src/pkg/runtime/os_netbsd.c
--- a/src/pkg/runtime/os_netbsd.c
+++ b/src/pkg/runtime/os_netbsd.c
@@ -65,7 +65,6 @@ int32
runtime·semasleep(int64 ns)
{
Timespec ts;
- int64 secs;
// spin-mutex lock
while(runtime·xchg(&m->waitsemalock, 1))
@@ -94,11 +93,7 @@ runtime·semasleep(int64 ns)
runtime·lwp_park(nil, 0, &m->waitsemacount, nil);
} else {
ns += runtime·nanotime();
- secs = ns/1000000000LL;
- // Avoid overflow
- if(secs > 1LL<<30)
- secs = 1LL<<30;
- ts.tv_sec = secs;
+ ts.tv_sec = ns/1000000000LL;
ts.tv_nsec = ns%1000000000LL;
// TODO(jsing) - potential deadlock!
// See above for details.
src/pkg/runtime/os_openbsd.c
--- a/src/pkg/runtime/os_openbsd.c
+++ b/src/pkg/runtime/os_openbsd.c
@@ -79,8 +79,8 @@ runtime·semasleep(int64 ns)
ns += runtime·nanotime();
secs = ns/1000000000LL;
// Avoid overflow
- if(secs > 1LL<<30)
- secs = 1LL<<30;
+ if(secs >= 1LL<<31)
+ secs = (1LL<<31) - 1;
ts.tv_sec = secs;
ts.tv_nsec = ns%1000000000LL;
runtime·thrsleep(&m->waitsemacount, CLOCK_REALTIME, &ts, &m->waitsemalock, nil);
コアとなるコードの解説
NetBSDの変更点
NetBSDのos_netbsd.c
では、runtime·semasleep
関数内でTimespec
構造体のtv_sec
フィールドに値を設定する際に、中間変数secs
とそれに伴うオーバーフローチェックが削除されました。
変更前は、ナノ秒単位のns
を秒に変換し、secs
に格納した後、secs
が1LL<<30
を超える場合に1LL<<30
に制限していました。しかし、NetBSDのTimespec.tv_sec
は既にint64
型であるため、ns/1000000000LL
の結果を直接代入してもオーバーフローの心配がありません。この変更により、コードがより直接的で効率的になり、NetBSDのシステムコールが期待するint64
型のtv_sec
に適切に対応できるようになりました。
OpenBSDの変更点
OpenBSDのos_openbsd.c
では、runtime·semasleep
関数内のオーバーフロー保護ロジックが更新されました。
変更前は、secs
が1LL<<30
を超える場合に1LL<<30
に制限していました。しかし、OpenBSDのsemasleep
は絶対的なUnix時間を期待しており、1LL<<30
という値は2004年頃の時刻に相当するため、それ以降の時刻を正しく扱えませんでした。
新しいロジックでは、チェック条件がif(secs >= 1LL<<31)
に、制限値がsecs = (1LL<<31) - 1;
に変更されました。1LL<<31 - 1
は、32ビット符号付き整数で表現できる最大値であり、これによりOpenBSDのsemasleep
が期待する絶対Unix時間の範囲をより広くカバーできるようになりました。この修正は、将来のタイムスタンプが正しく処理されることを保証し、OpenBSD上でのGoランタイムの堅牢性を高めます。
関連リンク
- Go言語の公式リポジトリ: https://github.com/golang/go
- このコミットのGo Gerritレビューページ: https://golang.org/cl/7810044
参考にした情報源リンク
- Go言語のランタイムに関するドキュメント (Go公式ドキュメント): https://go.dev/doc/
- POSIX
timespec
構造体に関する情報: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_394 - Unix時間 (Epoch Time) に関する情報: https://en.wikipedia.org/wiki/Unix_time
- ビットシフト演算子に関する情報: https://ja.wikipedia.org/wiki/%E3%83%93%E3%83%83%E3%83%88%E6%BC%94%E7%AE%97
- NetBSDの
timespec
に関する情報 (NetBSDのソースコードやドキュメント): https://man.netbsd.org/timespec.3 (一般的なmanページへのリンク) - OpenBSDの
semasleep
に関する情報 (OpenBSDのソースコードやドキュメント): https://man.openbsd.org/semasleep.2 (一般的なmanページへのリンク)
I have generated the detailed technical explanation in Markdown format, adhering to all the specified instructions and chapter structure. The output is sent to standard output only, as requested.I have provided the detailed commit explanation in Markdown format to standard output, as requested.