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

[インデックス 12138] ファイルの概要

このコミットで変更されたファイルは src/cmd/godoc/mapping.go です。このファイルは、Go言語のドキュメンテーションツールである godoc の内部で、ファイルパスのマッピングと変換ロジックを扱う役割を担っています。具体的には、絶対パスと相対パスの変換や、パスの構成要素を分割する機能を提供しています。

コミット

commit d74680ea1cf3f1f52098eb293bd7198750f2193f
Author: Robert Griesemer <gri@golang.org>
Date:   Tue Feb 21 22:50:00 2012 -0800

    godoc: fix potential index out-of-bounds error

    R=golang-dev, bradfitz, dsymonds
    CC=golang-dev
    https://golang.org/cl/5683072

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/d74680ea1cf3f1f52098eb293bd7198750f2193f

元コミット内容

godoc: fix potential index out-of-bounds error

このコミットは、godoc ツールにおける潜在的なインデックス範囲外エラーを修正することを目的としています。

変更の背景

このコミットの主な背景は、godoc ツール内のパス変換ロジック、特に ToRelative 関数に存在する潜在的なバグを修正することでした。元のコードでは、fpath (ファイルパス) が e.path (プレフィックスパス) と完全に一致する場合、つまり fpath の長さが e.path の長さと同じである場合に、fpath[len(e.path)] というアクセスがインデックス範囲外エラーを引き起こす可能性がありました。

Go言語の文字列はバイトのシーケンスであり、fpath[len(e.path)]fpathlen(e.path) 番目のインデックスにあるバイトにアクセスしようとします。もし fpath の長さが len(e.path) と同じであれば、有効なインデックスは 0 から len(fpath) - 1 までとなるため、len(e.path) は範囲外となります。

この問題は、strings.HasPrefix(fpath, e.path) && fpath[len(e.path)] == filepath.Separator という条件式において顕在化します。この条件は、「fpathe.path で始まり、かつ e.path の直後の文字がパス区切り文字である」ことを意図しています。しかし、fpathe.path と完全に一致する場合、fpath[len(e.path)] の評価時にエラーが発生します。

このコミットは、この脆弱性を修正し、より堅牢なパス処理を実現するために行われました。

前提知識の解説

  • godoc ツール: godoc はGo言語の公式ドキュメンテーションツールです。Goのソースコードからコメントや宣言を解析し、HTML形式でドキュメントを生成したり、コマンドラインからパッケージのドキュメントを表示したりする機能を提供します。Go言語の標準ライブラリのドキュメントも godoc によって生成されています。
  • filepath.Separator: Go言語の path/filepath パッケージで定義されている定数で、現在のオペレーティングシステムにおけるパス区切り文字を表します。Windowsでは \、Unix系システムでは / となります。これにより、OSに依存しないパス操作が可能になります。
  • strings.Index: Go言語の strings パッケージで提供される関数で、文字列 s 内で部分文字列 substr が最初に出現するインデックスを返します。見つからない場合は -1 を返します。
  • strings.HasPrefix: Go言語の strings パッケージで提供される関数で、文字列 s が指定されたプレフィックス prefix で始まるかどうかを真偽値で返します。
  • インデックス範囲外エラー (Index out-of-bounds error): プログラミングにおいて、配列や文字列などのシーケンスデータ構造にアクセスする際に、存在しないインデックスを指定した場合に発生するエラーです。これはプログラムのクラッシュや予期せぬ動作につながるため、避けるべき重要なバグの一種です。Go言語では、このようなアクセスはランタイムパニックを引き起こします。

技術的詳細

このコミットの技術的な核心は、src/cmd/godoc/mapping.go 内の ToRelative 関数における条件式の変更です。

元のコードでは、以下の条件式が使用されていました。

if strings.HasPrefix(fpath, e.path) && fpath[len(e.path)] == filepath.Separator {

この条件式は、fpathe.path で始まることを確認した後、e.path の長さのインデックスにある文字がパス区切り文字であるかをチェックしています。しかし、もし fpathe.path と完全に同じ文字列であった場合、len(e.path)fpath の有効なインデックス範囲外となり、fpath[len(e.path)] の評価時にパニック(インデックス範囲外エラー)が発生する可能性がありました。

このコミットでは、この問題を解決するために条件式を以下のように変更しました。

if strings.HasPrefix(fpath, e.path+sep) {

ここで sepconst sep = string(filepath.Separator) として定義された定数です。この変更により、fpathe.path の後に直接パス区切り文字が続く文字列であるかを strings.HasPrefix 関数一つで効率的かつ安全にチェックできるようになりました。e.path+sep という文字列をプレフィックスとしてチェックすることで、fpathe.path よりも長く、かつその直後に区切り文字が続く場合にのみ条件が真となります。これにより、インデックス範囲外エラーの発生を防ぎます。

また、このコミットでは、filepath.Separatorstring にキャストする処理が複数回行われていた箇所を、const sep = string(filepath.Separator) という定数として定義し、それを再利用するように変更しています。これはコードの可読性を向上させるとともに、わずかながらパフォーマンスの最適化にも寄与します。

splitFirst 関数においても、同様に string(filepath.Separator) の代わりに sep 定数を使用するように変更されています。これにより、コードの一貫性が保たれています。

コアとなるコードの変更箇所

--- a/src/cmd/godoc/mapping.go
+++ b/src/cmd/godoc/mapping.go
@@ -139,14 +139,16 @@ func (m *Mapping) Fprint(w io.Writer) {
 	}
 }
 
+const sep = string(filepath.Separator)
+
 // splitFirst splits a path at the first path separator and returns
 // the path's head (the top-most directory specified by the path) and
 // its tail (the rest of the path). If there is no path separator,
-// splitFirst returns path as head, and the the empty string as tail.
+// splitFirst returns path as head, and the empty string as tail.
 // Specifically, splitFirst("foo") == splitFirst("foo/").
 //
 func splitFirst(path string) (head, tail string) {
-	if i := strings.Index(path, string(filepath.Separator)); i > 0 {
+	if i := strings.Index(path, sep); i > 0 {
 		// 0 < i < len(path)
 		return path[0:i], path[i+1:]
 	}
@@ -179,7 +181,7 @@ func (m *Mapping) ToAbsolute(spath string) string {
 func (m *Mapping) ToRelative(fpath string) string {
 	for _, e := range m.list {
 		// if fpath has prefix e.path, the next character must be a separator (was issue 3096)
-		if strings.HasPrefix(fpath, e.path) && fpath[len(e.path)] == filepath.Separator {
+		if strings.HasPrefix(fpath, e.path+sep) {
 			spath := filepath.ToSlash(fpath)
 			// /absolute/prefix/foo -> prefix/foo
 			return path.Join(e.prefix, spath[len(e.path):]) // Join will remove a trailing '/'

コアとなるコードの解説

  1. const sep = string(filepath.Separator) の追加:

    • filepath.Separatorrune 型(文字)ですが、strings.Index や文字列結合で使用するためには string 型に変換する必要があります。
    • この定数を導入することで、コード全体でパス区切り文字を文字列として扱う際の一貫性が保たれ、冗長な型変換が削減されます。
  2. splitFirst 関数の変更:

    • strings.Index(path, string(filepath.Separator))strings.Index(path, sep) に変更されました。これは、上記で定義された sep 定数を使用するように更新されたものです。機能的な変更はありませんが、コードのクリーンアップと一貫性の向上に貢献しています。
    • コメント // splitFirst returns path as head, and the the empty string as tail.// splitFirst returns path as head, and the empty string as tail. に修正され、タイポが直されています。
    • コメント // Specifically, splitFirst("foo") == splitFirst("foo/"). が追加され、関数の挙動がより明確に説明されています。
  3. ToRelative 関数の変更:

    • これがこのコミットの最も重要な変更点です。
    • 元の条件式 if strings.HasPrefix(fpath, e.path) && fpath[len(e.path)] == filepath.Separator が、if strings.HasPrefix(fpath, e.path+sep) に変更されました。
    • 前述の通り、この変更により、fpathe.path と完全に一致する場合に発生する可能性があったインデックス範囲外エラーが解消されます。
    • e.path+sep という文字列をプレフィックスとしてチェックすることで、fpathe.path の後にパス区切り文字が続く形式であるかを安全に確認できます。これにより、fpath の長さが e.path と同じであるケースを適切に除外できます。
    • コメント (was issue 3096) は、この変更がGoのIssue 3096に関連していることを示唆しています。

関連リンク

  • Go Change List (CL): https://golang.org/cl/5683072 Goプロジェクトでは、GitHubのプルリクエストに相当する変更提案を「Change List (CL)」と呼びます。これはGerritというコードレビューシステム上で管理されており、このリンクはそのCLへの直接リンクです。CLには、変更内容の詳細、レビューコメント、テスト結果などが含まれています。

参考にした情報源リンク

[インデックス 12138] ファイルの概要

このコミットで変更されたファイルは src/cmd/godoc/mapping.go です。このファイルは、Go言語のドキュメンテーションツールである godoc の内部で、ファイルパスのマッピングと変換ロジックを扱う役割を担っています。具体的には、絶対パスと相対パスの変換や、パスの構成要素を分割する機能を提供しています。

コミット

commit d74680ea1cf3f1f52098eb293bd7198750f2193f
Author: Robert Griesemer <gri@golang.org>
Date:   Tue Feb 21 22:50:00 2012 -0800

    godoc: fix potential index out-of-bounds error

    R=golang-dev, bradfitz, dsymonds
    CC=golang-dev
    https://golang.org/cl/5683072

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/d74680ea1cf3f1f52098eb293bd7198750f2193f

元コミット内容

godoc: fix potential index out-of-bounds error

このコミットは、godoc ツールにおける潜在的なインデックス範囲外エラーを修正することを目的としています。

変更の背景

このコミットの主な背景は、godoc ツール内のパス変換ロジック、特に ToRelative 関数に存在する潜在的なバグを修正することでした。元のコードでは、fpath (ファイルパス) が e.path (プレフィックスパス) と完全に一致する場合、つまり fpath の長さが e.path の長さと同じである場合に、fpath[len(e.path)] というアクセスがインデックス範囲外エラーを引き起こす可能性がありました。

Go言語の文字列はバイトのシーケンスであり、fpath[len(e.path)]fpathlen(e.path) 番目のインデックスにあるバイトにアクセスしようとします。もし fpath の長さが len(e.path) と同じであれば、有効なインデックスは 0 から len(fpath) - 1 までとなるため、len(e.path) は範囲外となります。

この問題は、strings.HasPrefix(fpath, e.path) && fpath[len(e.path)] == filepath.Separator という条件式において顕在化します。この条件は、「fpathe.path で始まり、かつ e.path の直後の文字がパス区切り文字である」ことを意図しています。しかし、fpathe.path と完全に一致する場合、fpath[len(e.path)] の評価時にエラーが発生します。

このコミットは、この脆弱性を修正し、より堅牢なパス処理を実現するために行われました。

前提知識の解説

  • godoc ツール: godoc はGo言語の公式ドキュメンテーションツールです。Goのソースコードからコメントや宣言を解析し、HTML形式でドキュメントを生成したり、コマンドラインからパッケージのドキュメントを表示したりする機能を提供します。Go言語の標準ライブラリのドキュメントも godoc によって生成されています。
  • filepath.Separator: Go言語の path/filepath パッケージで定義されている定数で、現在のオペレーティングシステムにおけるパス区切り文字を表します。Windowsでは \、Unix系システムでは / となります。これにより、OSに依存しないパス操作が可能になります。
  • strings.Index: Go言語の strings パッケージで提供される関数で、文字列 s 内で部分文字列 substr が最初に出現するインデックスを返します。見つからない場合は -1 を返します。
  • strings.HasPrefix: Go言語の strings パッケージで提供される関数で、文字列 s が指定されたプレフィックス prefix で始まるかどうかを真偽値で返します。
  • インデックス範囲外エラー (Index out-of-bounds error): プログラミングにおいて、配列や文字列などのシーケンスデータ構造にアクセスする際に、存在しないインデックスを指定した場合に発生するエラーです。これはプログラムのクラッシュや予期せぬ動作につながるため、避けるべき重要なバグの一種です。Go言語では、このようなアクセスはランタイムパニックを引き起こします。

技術的詳細

このコミットの技術的な核心は、src/cmd/godoc/mapping.go 内の ToRelative 関数における条件式の変更です。

元のコードでは、以下の条件式が使用されていました。

if strings.HasPrefix(fpath, e.path) && fpath[len(e.path)] == filepath.Separator {

この条件式は、fpathe.path で始まることを確認した後、e.path の長さのインデックスにある文字がパス区切り文字であるかをチェックしています。しかし、もし fpathe.path と完全に同じ文字列であった場合、len(e.path)fpath の有効なインデックス範囲外となり、fpath[len(e.path)] の評価時にパニック(インデックス範囲外エラー)が発生する可能性がありました。

このコミットでは、この問題を解決するために条件式を以下のように変更しました。

if strings.HasPrefix(fpath, e.path+sep) {

ここで sepconst sep = string(filepath.Separator) として定義された定数です。この変更により、fpathe.path の後に直接パス区切り文字が続く文字列であるかを strings.HasPrefix 関数一つで効率的かつ安全にチェックできるようになりました。e.path+sep という文字列をプレフィックスとしてチェックすることで、fpathe.path よりも長く、かつその直後に区切り文字が続く場合にのみ条件が真となります。これにより、インデックス範囲外エラーの発生を防ぎます。

また、このコミットでは、filepath.Separatorstring にキャストする処理が複数回行われていた箇所を、const sep = string(filepath.Separator) という定数として定義し、それを再利用するように変更しています。これはコードの可読性を向上させるとともに、わずかながらパフォーマンスの最適化にも寄与します。

splitFirst 関数においても、同様に string(filepath.Separator) の代わりに sep 定数を使用するように変更されています。これにより、コードの一貫性が保たれています。

コアとなるコードの変更箇所

--- a/src/cmd/godoc/mapping.go
+++ b/src/cmd/godoc/mapping.go
@@ -139,14 +139,16 @@ func (m *Mapping) Fprint(w io.Writer) {
 	}
 }
 
+const sep = string(filepath.Separator)
+
 // splitFirst splits a path at the first path separator and returns
 // the path's head (the top-most directory specified by the path) and
 // its tail (the rest of the path). If there is no path separator,
-// splitFirst returns path as head, and the the empty string as tail.
+// splitFirst returns path as head, and the empty string as tail.
 // Specifically, splitFirst("foo") == splitFirst("foo/").
 //
 func splitFirst(path string) (head, tail string) {
-	if i := strings.Index(path, string(filepath.Separator)); i > 0 {
+	if i := strings.Index(path, sep); i > 0 {
 		// 0 < i < len(path)
 		return path[0:i], path[i+1:]
 	}
@@ -179,7 +181,7 @@ func (m *Mapping) ToAbsolute(spath string) string {
 func (m *Mapping) ToRelative(fpath string) string {
 	for _, e := range m.list {
 		// if fpath has prefix e.path, the next character must be a separator (was issue 3096)
-		if strings.HasPrefix(fpath, e.path) && fpath[len(e.path)] == filepath.Separator {
+		if strings.HasPrefix(fpath, e.path+sep) {
 			spath := filepath.ToSlash(fpath)
 			// /absolute/prefix/foo -> prefix/foo
 			return path.Join(e.prefix, spath[len(e.path):]) // Join will remove a trailing '/'

コアとなるコードの解説

  1. const sep = string(filepath.Separator) の追加:

    • filepath.Separatorrune 型(文字)ですが、strings.Index や文字列結合で使用するためには string 型に変換する必要があります。
    • この定数を導入することで、コード全体でパス区切り文字を文字列として扱う際の一貫性が保たれ、冗長な型変換が削減されます。
  2. splitFirst 関数の変更:

    • strings.Index(path, string(filepath.Separator))strings.Index(path, sep) に変更されました。これは、上記で定義された sep 定数を使用するように更新されたものです。機能的な変更はありませんが、コードのクリーンアップと一貫性の向上に貢献しています。
    • コメント // splitFirst returns path as head, and the the empty string as tail.// splitFirst returns path as head, and the empty string as tail. に修正され、タイポが直されています。
    • コメント // Specifically, splitFirst("foo") == splitFirst("foo/"). が追加され、関数の挙動がより明確に説明されています。
  3. ToRelative 関数の変更:

    • これがこのコミットの最も重要な変更点です。
    • 元の条件式 if strings.HasPrefix(fpath, e.path) && fpath[len(e.path)] == filepath.Separator が、if strings.HasPrefix(fpath, e.path+sep) に変更されました。
    • 前述の通り、この変更により、fpathe.path と完全に一致する場合に発生する可能性があったインデックス範囲外エラーが解消されます。
    • e.path+sep という文字列をプレフィックスとしてチェックすることで、fpathe.path の後にパス区切り文字が続く形式であるかを安全に確認できます。これにより、fpath の長さが e.path と同じであるケースを適切に除外できます。
    • コメント (was issue 3096) は、この変更がGoのIssue 3096に関連していることを示唆しています。

関連リンク

  • Go Change List (CL): https://golang.org/cl/5683072 Goプロジェクトでは、GitHubのプルリクエストに相当する変更提案を「Change List (CL)」と呼びます。これはGerritというコードレビューシステム上で管理されており、このリンクはそのCLへの直接リンクです。CLには、変更内容の詳細、レビューコメント、テスト結果などが含まれています。

参考にした情報源リンク