[インデックス 14497] ファイルの概要
このコミットは、Go言語のgo/printer
パッケージにおける潜在的なint32
オーバーフローの問題を修正するものです。具体的には、コードフォーマット時に「無限大」を示す値が使用される箇所で、その値がint32
の最大値を超えてしまう可能性があったため、より安全な値に置き換える変更が行われています。これにより、特定のビルド環境での破損(Fixes 386 build breakage
)が解消されました。
コミット
commit 4a1b8146687c1e73786857af36d8b4f1053aa4e6
Author: Robert Griesemer <gri@golang.org>
Date: Mon Nov 26 14:20:05 2012 -0800
go/printer: don't use 'infinity' value when it might lead to int32 overflow
Fixes 386 build breakage.
R=rsc
CC=golang-dev
https://golang.org/cl/6844091
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/4a1b8146687c1e73786857af36d8b4f1053aa4e6
元コミット内容
go/printer: don't use 'infinity' value when it might lead to int32 overflow
Fixes 386 build breakage.
変更の背景
このコミットは、Go言語の標準ライブラリの一部であるgo/printer
パッケージにおいて発生していたビルドエラー(Fixes 386 build breakage
)を修正するために行われました。このエラーは、特定の状況下でコードの整形処理を行う際に、内部的に「無限大」を意味する値がint32
型の変数に格納されようとした結果、オーバーフローが発生し、ビルドが失敗するという問題でした。
特に「386ビルドの破損」と明記されていることから、これは32ビットアーキテクチャ(Intel 80386互換プロセッサ)をターゲットとしたビルド環境で顕在化する問題であったと推測されます。32ビットシステムではint
型が通常32ビット幅であり、表現できる数値の範囲が限られています。この範囲を超える値を代入しようとすると、オーバーフローが発生し、予期せぬ動作やクラッシュ、あるいはコンパイルエラーを引き起こす可能性があります。
go/printer
は、Goのソースコードを整形(フォーマット)するためのパッケージであり、コードの可読性を高めるために、ブロックの配置や改行の有無などを決定するロジックを含んでいます。このロジックの中で、特定の条件(例えば、ブロックが1行に収まらないようにする、コメントが存在するため1行にしないなど)を満たす場合に、そのブロックのサイズを「無限大」として扱うことで、1行に整形されないように制御していました。しかし、この「無限大」の表現方法がint32
の限界を超えていたことが問題の根源でした。
前提知識の解説
Go言語のgo/printer
パッケージ
go/printer
パッケージは、Go言語の標準ライブラリの一部であり、Goのソースコードを整形(フォーマット)するために使用されます。これは、go fmt
コマンドの基盤となるパッケージの一つです。go/printer
は、抽象構文木(AST: Abstract Syntax Tree)を受け取り、それをGoの公式なスタイルガイドに沿って整形されたソースコードとして出力する役割を担います。
コードの整形においては、改行の挿入、インデントの調整、空白の配置など、多くのルールが適用されます。特に、if
文やfor
文などのブロック(ast.BlockStmt
)を1行にまとめるか、複数行に展開するかは、可読性に大きく影響しまするため、go/printer
はこれらの判断を内部的に行います。
int32
と整数オーバーフロー
int32
は、32ビット符号付き整数型を指します。これは、-2,147,483,648から2,147,483,647までの整数値を表現できます。コンピュータのメモリ上で32ビット(4バイト)の領域を使って数値を格納します。
整数オーバーフローとは、あるデータ型で表現できる数値の範囲を超えた値を代入しようとしたときに発生する現象です。符号付き整数型の場合、最大値を超えると最小値に「ラップアラウンド」したり、最小値を下回ると最大値に「ラップアラウンド」したりすることがあります。C言語やGo言語のような一部の言語では、符号付き整数のオーバーフローは未定義動作(Undefined Behavior)となることがあり、プログラムのクラッシュ、予期せぬ結果、セキュリティ脆弱性など、様々な問題を引き起こす可能性があります。
このコミットの文脈では、go/printer
が内部的に「無限大」を表現するために、int32
の最大値を超えるような大きな値を使用していたことが、32ビットシステムでのオーバーフローの原因となりました。
ビルド破損(Build Breakage)
ビルド破損とは、ソフトウェアのコンパイルやリンクのプロセスが失敗し、実行可能なバイナリが生成できない状態を指します。これは、コードの構文エラー、依存関係の欠落、環境設定の問題、そして本件のようなランタイムエラー(コンパイル時に検出される可能性のあるオーバーフローなど)によって引き起こされることがあります。
「386ビルドの破損」は、特に32ビットアーキテクチャ(i386)向けのビルドで問題が発生したことを示しており、これは32ビット環境特有の制約(例: int32
の範囲)が原因であることを強く示唆しています。
技術的詳細
このコミットの核心は、go/printer
パッケージ内のbodySize
関数における「無限大」の表現方法の変更です。
bodySize
関数は、ast.BlockStmt
(抽象構文木のブロックステートメント、例えば{ ... }
のようなコードブロック)のサイズを推定し、そのブロックを1行にまとめるべきか、複数行に展開すべきかを判断するために使用されます。
元のコードでは、特定の条件(例えば、開始ブレースと終了ブレースが異なる行にある場合、またはステートメントが多すぎる場合、あるいはブロック内にコメントがある場合)において、そのブロックを「1行にまとめるべきではない」と判断するために、infinity
という定数(おそらくint32
の最大値に近い、非常に大きな値)を返していました。これは、そのブロックのサイズが事実上「無限大」であると見なすことで、1行に収めるためのサイズ計算を失敗させ、結果として複数行に整形させるというロジックでした。
しかし、このinfinity
の値がint32
の最大値を超えていたため、32ビットシステムでbodySize
関数の戻り値がint32
型の変数に代入される際にオーバーフローが発生し、ビルドが失敗していました。
このコミットでは、infinity
の代わりにmaxSize + 1
という値を使用するように変更されました。maxSize
はbodySize
関数に渡される引数であり、ブロックの最大許容サイズを示します。maxSize + 1
を返すことで、そのブロックがmaxSize
よりも大きい、つまり「1行に収まらない」という意図を安全に表現できるようになります。この値はint32
の範囲内に収まるため、オーバーフローの問題が解消されます。
この変更は、go/printer
のロジックの意図(特定のブロックを1行にしない)を維持しつつ、数値的な安全性を確保するためのものです。
コアとなるコードの変更箇所
変更はsrc/pkg/go/printer/nodes.go
ファイル内のbodySize
関数にあります。
--- a/src/pkg/go/printer/nodes.go
+++ b/src/pkg/go/printer/nodes.go
@@ -1431,11 +1431,11 @@ func (p *printer) bodySize(b *ast.BlockStmt, maxSize int) int {
pos2 := b.Rbrace
if pos1.IsValid() && pos2.IsValid() && p.lineFor(pos1) != p.lineFor(pos2) {
// opening and closing brace are on different lines - don't make it a one-liner
- return infinity
+ return maxSize + 1
}
if len(b.List) > 5 || p.commentBefore(p.posFor(pos2)) {
// too many statements or there is a comment inside - don't make it a one-liner
- return infinity
+ return maxSize + 1
}
// otherwise, estimate body size
bodySize := 0
コアとなるコードの解説
bodySize
関数は、*ast.BlockStmt
型のb
とmaxSize
(整数)を引数として受け取ります。この関数は、与えられたコードブロックb
の推定サイズを返します。
変更された2つのif
ブロックは、それぞれ特定の条件が満たされた場合に、そのブロックを1行に整形すべきではないと判断するロジックです。
-
最初の
if
ブロック:if pos1.IsValid() && pos2.IsValid() && p.lineFor(pos1) != p.lineFor(pos2) { // opening and closing brace are on different lines - don't make it a one-liner return maxSize + 1 // 変更前: return infinity }
この条件は、ブロックの開始ブレース(
pos1
)と終了ブレース(pos2
)が異なる行にある場合に真となります。つまり、ブロックが既に複数行にわたっている場合、それを強制的に1行にまとめようとしない、という意図です。以前はinfinity
を返していましたが、maxSize + 1
を返すことで、このブロックのサイズがmaxSize
よりも大きいと見なされ、結果として1行に整形されないようにします。 -
2番目の
if
ブロック:if len(b.List) > 5 || p.commentBefore(p.posFor(pos2)) { // too many statements or there is a comment inside - don't make it a one-liner return maxSize + 1 // 変更前: return infinity }
この条件は、ブロック内のステートメントの数が5を超える場合(
len(b.List) > 5
)、または終了ブレースの前にコメントが存在する場合(p.commentBefore(p.posFor(pos2))
)に真となります。これらの場合も、ブロックを1行にまとめるのは可読性を損なうため避けるべき、という意図です。ここでもinfinity
からmaxSize + 1
への変更が行われ、同様にオーバーフローを回避しつつ、整形ロジックの意図を維持しています。
これらの変更により、bodySize
関数が返す値が常にint32
の範囲内に収まるようになり、32ビットシステムでのビルド破損が解消されました。
関連リンク
- Go言語の
go/printer
パッケージのドキュメント: https://pkg.go.dev/go/printer - Go言語の
go/ast
パッケージのドキュメント: https://pkg.go.dev/go/ast - Go言語の
go fmt
コマンドについて: https://go.dev/blog/go-fmt
参考にした情報源リンク
- Go言語の公式リポジトリ: https://github.com/golang/go
- Go言語のコードレビューシステム(Gerrit)のCL(Change-list): https://golang.org/cl/6844091 (コミットメッセージに記載されているリンク)
- 整数オーバーフローに関する一般的な情報 (例: Wikipediaなど)
- 32ビットアーキテクチャに関する一般的な情報 (例: Wikipediaなど)
[インデックス 14497] ファイルの概要
このコミットは、Go言語のgo/printer
パッケージにおける潜在的なint32
オーバーフローの問題を修正するものです。具体的には、コードフォーマット時に「無限大」を示す値が使用される箇所で、その値がint32
の最大値を超えてしまう可能性があったため、より安全な値に置き換える変更が行われています。これにより、特定のビルド環境での破損(Fixes 386 build breakage
)が解消されました。
コミット
commit 4a1b8146687c1e73786857af36d8b4f1053aa4e6
Author: Robert Griesemer <gri@golang.org>
Date: Mon Nov 26 14:20:05 2012 -0800
go/printer: don't use 'infinity' value when it might lead to int32 overflow
Fixes 386 build breakage.
R=rsc
CC=golang-dev
https://golang.org/cl/6844091
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/4a1b8146687c1e73786857af36d8b4f1053aa4e6
元コミット内容
go/printer: don't use 'infinity' value when it might lead to int32 overflow
Fixes 386 build breakage.
変更の背景
このコミットは、Go言語の標準ライブラリの一部であるgo/printer
パッケージにおいて発生していたビルドエラー(Fixes 386 build breakage
)を修正するために行われました。このエラーは、特定の状況下でコードの整形処理を行う際に、内部的に「無限大」を意味する値がint32
型の変数に格納されようとした結果、オーバーフローが発生し、ビルドが失敗するという問題でした。
特に「386ビルドの破損」と明記されていることから、これは32ビットアーキテクチャ(Intel 80386互換プロセッサ)をターゲットとしたビルド環境で顕在化する問題であったと推測されます。32ビットシステムではint
型が通常32ビット幅であり、表現できる数値の範囲が限られています。この範囲を超える値を代入しようとすると、オーバーフローが発生し、予期せぬ動作やクラッシュ、あるいはコンパイルエラーを引き起こす可能性があります。
go/printer
は、Goのソースコードを整形(フォーマット)するためのパッケージであり、コードの可読性を高めるために、ブロックの配置や改行の有無などを決定するロジックを含んでいます。このロジックの中で、特定の条件(例えば、ブロックが1行に収まらないようにする、コメントが存在するため1行にしないなど)を満たす場合に、そのブロックのサイズを「無限大」として扱うことで、1行に整形されないように制御していました。しかし、この「無限大」の表現方法がint32
の限界を超えていたことが問題の根源でした。
前提知識の解説
Go言語のgo/printer
パッケージ
go/printer
パッケージは、Go言語の標準ライブラリの一部であり、Goのソースコードを整形(フォーマット)するために使用されます。これは、go fmt
コマンドの基盤となるパッケージの一つです。go/printer
は、抽象構文木(AST: Abstract Syntax Tree)を受け取り、それをGoの公式なスタイルガイドに沿って整形されたソースコードとして出力する役割を担います。
コードの整形においては、改行の挿入、インデントの調整、空白の配置など、多くのルールが適用されます。特に、if
文やfor
文などのブロック(ast.BlockStmt
)を1行にまとめるか、複数行に展開するかは、可読性に大きく影響しまするため、go/printer
はこれらの判断を内部的に行います。
int32
と整数オーバーフロー
int32
は、32ビット符号付き整数型を指します。これは、-2,147,483,648から2,147,483,647までの整数値を表現できます。コンピュータのメモリ上で32ビット(4バイト)の領域を使って数値を格納します。
整数オーバーフローとは、あるデータ型で表現できる数値の範囲を超えた値を代入しようとしたときに発生する現象です。符号付き整数型の場合、最大値を超えると最小値に「ラップアラウンド」したり、最小値を下回ると最大値に「ラップアラウンド」したりすることがあります。C言語やGo言語のような一部の言語では、符号付き整数のオーバーフローは未定義動作(Undefined Behavior)となることがあり、プログラムのクラッシュ、予期せぬ結果、セキュリティ脆弱性など、様々な問題を引き起こす可能性があります。
このコミットの文脈では、go/printer
が内部的に「無限大」を表現するために、int32
の最大値を超えるような大きな値を使用していたことが、32ビットシステムでのオーバーフローの原因となりました。
ビルド破損(Build Breakage)
ビルド破損とは、ソフトウェアのコンパイルやリンクのプロセスが失敗し、実行可能なバイナリが生成できない状態を指します。これは、コードの構文エラー、依存関係の欠落、環境設定の問題、そして本件のようなランタイムエラー(コンパイル時に検出される可能性のあるオーバーフローなど)によって引き起こされることがあります。
「386ビルドの破損」は、特に32ビットアーキテクチャ(i386)向けのビルドで問題が発生したことを示しており、これは32ビット環境特有の制約(例: int32
の範囲)が原因であることを強く示唆しています。
技術的詳細
このコミットの核心は、go/printer
パッケージ内のbodySize
関数における「無限大」の表現方法の変更です。
bodySize
関数は、ast.BlockStmt
(抽象構文木のブロックステートメント、例えば{ ... }
のようなコードブロック)のサイズを推定し、そのブロックを1行にまとめるべきか、複数行に展開すべきかを判断するために使用されます。
元のコードでは、特定の条件(例えば、開始ブレースと終了ブレースが異なる行にある場合、またはステートメントが多すぎる場合、あるいはブロック内にコメントがある場合)において、そのブロックを「1行にまとめるべきではない」と判断するために、infinity
という定数(おそらくint32
の最大値に近い、非常に大きな値)を返していました。これは、そのブロックのサイズが事実上「無限大」であると見なすことで、1行に収めるためのサイズ計算を失敗させ、結果として複数行に整形させるというロジックでした。
しかし、このinfinity
の値がint32
の最大値を超えていたため、32ビットシステムでbodySize
関数の戻り値がint32
型の変数に代入される際にオーバーフローが発生し、ビルドが失敗していました。
このコミットでは、infinity
の代わりにmaxSize + 1
という値を使用するように変更されました。maxSize
はbodySize
関数に渡される引数であり、ブロックの最大許容サイズを示します。maxSize + 1
を返すことで、そのブロックがmaxSize
よりも大きい、つまり「1行に収まらない」という意図を安全に表現できるようになります。この値はint32
の範囲内に収まるため、オーバーフローの問題が解消されます。
この変更は、go/printer
のロジックの意図(特定のブロックを1行にしない)を維持しつつ、数値的な安全性を確保するためのものです。
コアとなるコードの変更箇所
変更はsrc/pkg/go/printer/nodes.go
ファイル内のbodySize
関数にあります。
--- a/src/pkg/go/printer/nodes.go
+++ b/src/pkg/go/printer/nodes.go
@@ -1431,11 +1431,11 @@ func (p *printer) bodySize(b *ast.BlockStmt, maxSize int) int {
pos2 := b.Rbrace
if pos1.IsValid() && pos2.IsValid() && p.lineFor(pos1) != p.lineFor(pos2) {
// opening and closing brace are on different lines - don't make it a one-liner
- return infinity
+ return maxSize + 1
}
if len(b.List) > 5 || p.commentBefore(p.posFor(pos2)) {
// too many statements or there is a comment inside - don't make it a one-liner
- return infinity
+ return maxSize + 1
}
// otherwise, estimate body size
bodySize := 0
コアとなるコードの解説
bodySize
関数は、*ast.BlockStmt
型のb
とmaxSize
(整数)を引数として受け取ります。この関数は、与えられたコードブロックb
の推定サイズを返します。
変更された2つのif
ブロックは、それぞれ特定の条件が満たされた場合に、そのブロックを1行に整形すべきではないと判断するロジックです。
-
最初の
if
ブロック:if pos1.IsValid() && pos2.IsValid() && p.lineFor(pos1) != p.lineFor(pos2) { // opening and closing brace are on different lines - don't make it a one-liner return maxSize + 1 // 変更前: return infinity }
この条件は、ブロックの開始ブレース(
pos1
)と終了ブレース(pos2
)が異なる行にある場合に真となります。つまり、ブロックが既に複数行にわたっている場合、それを強制的に1行にまとめようとしない、という意図です。以前はinfinity
を返していましたが、maxSize + 1
を返すことで、このブロックのサイズがmaxSize
よりも大きいと見なされ、結果として1行に整形されないようにします。 -
2番目の
if
ブロック:if len(b.List) > 5 || p.commentBefore(p.posFor(pos2)) { // too many statements or there is a comment inside - don't make it a one-liner return maxSize + 1 // 変更前: return infinity }
この条件は、ブロック内のステートメントの数が5を超える場合(
len(b.List) > 5
)、または終了ブレースの前にコメントが存在する場合(p.commentBefore(p.posFor(pos2))
)に真となります。これらの場合も、ブロックを1行にまとめるのは可読性を損なうため避けるべき、という意図です。ここでもinfinity
からmaxSize + 1
への変更が行われ、同様にオーバーフローを回避しつつ、整形ロジックの意図を維持しています。
これらの変更により、bodySize
関数が返す値が常にint32
の範囲内に収まるようになり、32ビットシステムでのビルド破損が解消されました。
関連リンク
- Go言語の
go/printer
パッケージのドキュメント: https://pkg.go.dev/go/printer - Go言語の
go/ast
パッケージのドキュメント: https://pkg.go.dev/go/ast - Go言語の
go fmt
コマンドについて: https://go.dev/blog/go-fmt
参考にした情報源リンク
- Go言語の公式リポジトリ: https://github.com/golang/go
- Go言語のコードレビューシステム(Gerrit)のCL(Change-list): https://golang.org/cl/6844091 (コミットメッセージに記載されているリンク)
- 整数オーバーフローに関する一般的な情報 (例: Wikipediaなど)
- 32ビットアーキテクチャに関する一般的な情報 (例: Wikipediaなど)