Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 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関数に渡されるタイムアウト値の計算と、その値に対するオーバーフロー保護のロジックにありました。特に、以下の点が課題となっていました。

  1. NetBSDにおけるtv_secの型: NetBSDでは、タイムスタンプの秒部分を格納するtv_secフィールドが既にint64型であるため、以前のコードで行われていたint64へのキャストやオーバーフローチェックが不要でした。これは冗長であるだけでなく、潜在的に誤った挙動を引き起こす可能性がありました。
  2. 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の具体的な型(int32int64かなど)は、システムやコンパイラによって異なります。
  • Unix時間 (Epoch Time): 1970年1月1日00:00:00 UTC(協定世界時)からの経過秒数で時間を表すシステムです。多くのUNIX系システムで内部的に時間を扱う際に使用されます。
  • オーバーフロー保護 (Overflow Protection): 数値計算において、変数が格納できる最大値を超えてしまうこと(オーバーフロー)を防ぐための仕組みです。オーバーフローが発生すると、予期せぬ値になったり、プログラムがクラッシュしたりする可能性があります。
  • ビットシフト演算子 (<<): 1LL << 30のような表記は、ビットシフト演算子を使用しています。LLlong long型であることを示し、1を左に30ビットシフトするという意味です。これは2の30乗、つまり1,073,741,824という値を表します。同様に1LL << 312の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型の変数に格納していました。その後、secs1LL<<30(約10億秒)を超える場合に、secs1LL<<30に制限するというオーバーフロー保護ロジックがありました。

// 変更前
int64 secs;
// ...
secs = ns/1000000000LL;
// Avoid overflow
if(secs > 1LL<<30)
    secs = 1LL<<30;
ts.tv_sec = secs;

このコミットでは、以下の変更が行われました。

  1. int64 secs; の宣言が削除されました。
  2. ts.tv_sec = secs; の行が ts.tv_sec = ns/1000000000LL; に直接変更されました。
  3. それに伴い、// 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;

変更点は以下の通りです。

  1. オーバーフローチェックの条件が if(secs > 1LL<<30) から if(secs >= 1LL<<31) に変更されました。
  2. オーバーフロー時の制限値が 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に格納した後、secs1LL<<30を超える場合に1LL<<30に制限していました。しかし、NetBSDのTimespec.tv_secは既にint64型であるため、ns/1000000000LLの結果を直接代入してもオーバーフローの心配がありません。この変更により、コードがより直接的で効率的になり、NetBSDのシステムコールが期待するint64型のtv_secに適切に対応できるようになりました。

OpenBSDの変更点

OpenBSDのos_openbsd.cでは、runtime·semasleep関数内のオーバーフロー保護ロジックが更新されました。

変更前は、secs1LL<<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ランタイムの堅牢性を高めます。

関連リンク

参考にした情報源リンク

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.