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

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

このコミットは、Go言語の標準ライブラリである path/filepath パッケージ内の Match 関数における、不正な文字範囲(character range)のパース処理に関するパニック(panic)を修正するものです。具体的には、以下のファイルが変更されました。

  • src/pkg/path/filepath/match.go: Match 関数の主要なロジックが含まれるファイルで、パニックを引き起こす可能性のある条件をチェックするコードが追加されました。
  • src/pkg/path/filepath/match_test.go: Match 関数のテストファイルで、今回の修正によって対処される不正なパターンに関する新しいテストケースが追加されました。
  • src/pkg/path/match_test.go: path パッケージのテストファイルで、filepath パッケージと同様に、不正なパターンに関する新しいテストケースが追加されました。これは、path パッケージと filepath パッケージが類似のパターンマッチングロジックを共有しているためと考えられます。

コミット

commit 27032fddee27dd3b02437b118e50c66586052830
Author: Pieter Droogendijk <pieter@binky.org.uk>
Date:   Wed Jul 31 16:58:28 2013 +1000

    path/filepath: Panic in Match when parsing invalid character range.
    
    Fixes #5668.
    
    R=golang-dev, rsc, r
    CC=golang-dev
    https://golang.org/cl/12001056

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

https://github.com/golang/go/commit/27032fddee27dd3b02437b118e50c66586052830

元コミット内容

path/filepath: Panic in Match when parsing invalid character range. Fixes #5668.

R=golang-dev, rsc, r CC=golang-dev https://golang.org/cl/12001056

変更の背景

このコミットの背景には、Go言語の path/filepath パッケージの Match 関数が、特定の不正なパターン文字列を処理する際にパニックを引き起こすというバグが存在していました。具体的には、[ で始まり、その後に適切な文字範囲の定義や閉じ括弧 ] が続かないようなパターン(例: [[^)が入力された場合に、内部的なパースロジックが期待する文字を見つけられず、インデックス範囲外アクセスなどのエラーが発生し、結果としてプログラムがクラッシュ(パニック)していました。

この問題は、GoのIssueトラッカーで #5668 として報告されていました。パニックは、プログラムの予期せぬ終了を意味し、安定性やセキュリティ上の問題につながる可能性があります。特に、ユーザーからの入力や外部システムからのデータに基づいてパターンマッチングを行うアプリケーションでは、悪意のある入力によってサービス拒否攻撃(Denial of Service, DoS)を引き起こす可能性も考えられます。

このコミットは、このような不正なパターン入力に対する堅牢性を高め、パニックを回避して ErrBadPattern という適切なエラーを返すようにすることで、プログラムの安定性を向上させることを目的としています。

前提知識の解説

1. path/filepath パッケージと Match 関数

Go言語の標準ライブラリである path/filepath パッケージは、ファイルパスの操作やパターンマッチング機能を提供します。このパッケージは、オペレーティングシステムに依存しないパス操作(path パッケージ)と、オペレーティングシステムに依存するパス操作(filepath パッケージ)の両方を含んでいます。

filepath.Match(pattern, name string) (matched bool, err error) 関数は、シェル形式のパターンマッチングを行います。これは、Unixシェルで使われるグロブ(glob)パターンに似ています。

  • *: 任意の0個以上の文字にマッチします。
  • ?: 任意の1文字にマッチします。
  • [seq]: seq 内の任意の1文字にマッチします。seq は文字のリスト(例: [abc])または文字の範囲(例: [a-z])を指定できます。
  • [^seq]: seq 内の文字以外の任意の1文字にマッチします。

2. グロブ(Glob)パターン

グロブパターンは、ファイル名やパスをマッチさせるための簡易的なパターンマッチング構文です。正規表現よりもシンプルで、主にファイルシステムの操作で利用されます。path/filepath.Match 関数は、このグロブパターンを解釈してマッチングを行います。

3. 文字範囲(Character Range)

グロブパターンにおける文字範囲は、[a-z] のように角括弧 [] を使って表現されます。これは、指定された範囲内の任意の1文字にマッチすることを意味します。例えば、[0-9] は任意の数字にマッチし、[A-Za-z] は任意の大文字または小文字のアルファベットにマッチします。

また、[^...] のように ^ を先頭に置くことで、指定された文字範囲に含まれない文字にマッチさせる「否定」の表現も可能です。

4. Goにおけるパニック(Panic)

Go言語におけるパニックは、プログラムの実行中に回復不可能なエラーが発生したことを示すメカニズムです。パニックが発生すると、通常のプログラムフローは中断され、遅延関数(defer)が実行された後、プログラムはクラッシュします。これは、C++における例外やJavaにおける未捕捉の例外に似ていますが、Goでは通常、エラーは戻り値として明示的に処理されるべきであり、パニックは予期せぬ、回復不能な状況(例: nilポインタ参照、インデックス範囲外アクセス)のために予約されています。

このコミットで修正された問題は、不正なパターン文字列のパース中に、内部的なデータ構造への不正なアクセスが発生し、それがパニックを引き起こしていたと考えられます。

技術的詳細

path/filepath.Match 関数は、入力されたパターン文字列をチャンク(部分文字列)に分割し、それぞれを順に処理していきます。文字範囲 [...] の処理は、matchChunk 関数内で特に注意が必要です。

元の実装では、[ が検出された後、その後の文字が文字範囲の定義(例: a-z)や否定の ^、そして閉じ括弧 ] であることを期待してパースを進めます。しかし、パターン文字列が [[^ のように途中で終わっている場合、つまり、閉じ括弧 ] や文字範囲の定義が不足している場合に、パースロジックが期待する文字を読み取ろうとして、chunk スライスのインデックス範囲外にアクセスしようとする可能性がありました。

例えば、パターンが [ の場合、matchChunk[ を検出した後、次の文字を読み込もうとしますが、chunk はすでに空になっています。この状態で chunk[0] のようなアクセスを試みると、Goランタイムは「インデックス範囲外」のエラーを検出し、パニックを引き起こします。

このコミットは、このような状況を事前に検出し、パニックを回避するためのチェックを追加しています。具体的には、[ を検出した後、chunk の長さが0になった場合(つまり、[ の直後にパターン文字列が終了している場合)に、すぐに ErrBadPattern を返すように変更されました。これにより、不正なパターンが入力されても、プログラムがクラッシュすることなく、適切なエラーハンドリングが可能になります。

この修正は、matchChunk 関数内の case '[': ブロックに適用され、パターンが [ で始まった直後に chunk が空になるケースを捕捉します。

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

変更は src/pkg/path/filepath/match.go ファイルの matchChunk 関数内、case '[': のブロックにあります。

--- a/src/pkg/path/filepath/match.go
+++ b/src/pkg/path/filepath/match.go
@@ -132,6 +132,12 @@ func matchChunk(chunk, s string) (rest string, ok bool, err error) {
 			r, n := utf8.DecodeRuneInString(s)
 			s = s[n:]
 			chunk = chunk[1:]
+			// We can't end right after '[', we're expecting at least
+			// a closing bracket and possibly a caret.
+			if len(chunk) == 0 {
+				err = ErrBadPattern
+				return
+			}
 			// possibly negated
 			negated := chunk[0] == '^'
 			if negated {

コアとなるコードの解説

追加されたコードは以下の部分です。

			// We can't end right after '[', we're expecting at least
			// a closing bracket and possibly a caret.
			if len(chunk) == 0 {
				err = ErrBadPattern
				return
			}

このコードは、パターン文字列の現在のチャンク chunk[ 文字を処理した直後に空になったかどうかをチェックしています。

  1. r, n := utf8.DecodeRuneInString(s): これは、マッチ対象の文字列 s から次のUTF-8ルーン(文字)をデコードしています。この行自体は直接的な変更点ではありませんが、コンテキストとして重要です。
  2. s = s[n:]: マッチ対象の文字列 s を、デコードしたルーンの分だけ進めます。
  3. chunk = chunk[1:]: パターン文字列の現在のチャンク chunk を、[ 文字を消費した分だけ進めます。つまり、chunk[ の次の文字から始まるようになります。
  4. if len(chunk) == 0: ここが今回の修正の核心です。[ を消費した直後に chunk が空になった場合、それはパターンが [ で終わっていることを意味します(例: pattern = "[")。
  5. err = ErrBadPattern: この条件が真の場合、つまり不正なパターンが検出された場合、ErrBadPattern というエラーを設定します。ErrBadPatternpath/filepath パッケージで定義されているエラーで、パターンが不正であることを示します。
  6. return: エラーを設定した後、関数を即座に終了します。これにより、その後の不正なインデックスアクセスを防ぎ、パニックを回避します。

この変更により、Match 関数は、[ の後に続くべき文字(閉じ括弧 ] や文字範囲の定義)が存在しない場合に、パニックを起こす代わりに、明確なエラーを返すようになりました。これにより、不正なパターン入力に対する堅牢性が向上し、アプリケーションの安定性が確保されます。

また、テストファイル src/pkg/path/filepath/match_test.gosrc/pkg/path/match_test.go には、以下の新しいテストケースが追加されています。

	{"[", "a", false, ErrBadPattern},
	{"[^", "a", false, ErrBadPattern},
	{"[^bc", "a", false, ErrBadPattern},
	{"a[", "a", false, nil},
	{"a[", "ab", false, ErrBadPattern},

これらのテストケースは、今回の修正が意図通りに機能し、不正なパターンに対して ErrBadPattern を返すことを検証しています。特に {"[", "a", false, ErrBadPattern} は、[ のみがパターンとして与えられた場合にエラーが返されることを確認しています。

関連リンク

  • Go言語の path/filepath パッケージのドキュメント: https://pkg.go.dev/path/filepath
  • Go言語におけるエラーハンドリングとパニックに関する公式ドキュメント(Go Effective Goなど)

参考にした情報源リンク