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

[インデックス 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という値を使用するように変更されました。maxSizebodySize関数に渡される引数であり、ブロックの最大許容サイズを示します。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型のbmaxSize(整数)を引数として受け取ります。この関数は、与えられたコードブロックbの推定サイズを返します。

変更された2つのifブロックは、それぞれ特定の条件が満たされた場合に、そのブロックを1行に整形すべきではないと判断するロジックです。

  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. 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言語の公式リポジトリ: 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という値を使用するように変更されました。maxSizebodySize関数に渡される引数であり、ブロックの最大許容サイズを示します。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型のbmaxSize(整数)を引数として受け取ります。この関数は、与えられたコードブロックbの推定サイズを返します。

変更された2つのifブロックは、それぞれ特定の条件が満たされた場合に、そのブロックを1行に整形すべきではないと判断するロジックです。

  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. 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言語の公式リポジトリ: https://github.com/golang/go
  • Go言語のコードレビューシステム(Gerrit)のCL(Change-list): https://golang.org/cl/6844091 (コミットメッセージに記載されているリンク)
  • 整数オーバーフローに関する一般的な情報 (例: Wikipediaなど)
  • 32ビットアーキテクチャに関する一般的な情報 (例: Wikipediaなど)