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

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

このコミットは、Go言語の strconv パッケージにおける UnquoteChar 関数の挙動を修正し、不正な8進数エスケープシーケンス(例: \9)をアンクォートする際に ErrSyntax エラーを返すように変更します。これにより、Goコンパイラが文字列リテラル内で同様の不正なシーケンスを検出した場合の挙動と一貫性が保たれ、Issue 2658 で報告された問題が解決されます。

コミット

commit cbf4f4b8d03493c112b472b8fcf3d499dc4e6bc9
Author: Sameer Ajmani <sameer@golang.org>
Date:   Mon Jan 9 19:55:18 2012 -0500

    strconv: return ErrSyntax when unquoting illegal octal sequences.  This
    is consistent with what the Go compiler returns when such sequences
    appear in string literals.
    
    Fixes #2658.
    
    R=golang-dev, rsc, r, r, nigeltao
    CC=golang-dev
    https://golang.org/cl/5530051

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

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

元コミット内容

strconv: return ErrSyntax when unquoting illegal octal sequences.  This
is consistent with what the Go compiler returns when such sequences
appear in string literals.

Fixes #2658.

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

変更の背景

Go言語では、文字列リテラル内で特殊文字を表現するためにエスケープシーケンスが用いられます。特に、\ooo の形式は8進数エスケープシーケンスとして、3桁までの8進数で文字コードを指定します。例えば、\101 はASCII文字の 'A' を表します。

Goコンパイラは、文字列リテラル内で \9 のように8進数として不正な数字(8や9)が続くシーケンスを検出した場合、コンパイルエラーを発生させます。これは、言語仕様に則った厳密なチェックです。

一方、Goの標準ライブラリである strconv パッケージの UnquoteChar 関数は、引用符で囲まれた文字列(例えば、Goの文字列リテラルやJSON文字列など)から文字をアンクォート(エスケープ解除)する機能を提供します。この関数は、文字列を解析し、エスケープシーケンスを実際の文字に変換する役割を担っています。

このコミット以前は、UnquoteChar 関数が不正な8進数エスケープシーケンス(例: \9, \19, \129)を処理する際に、Goコンパイラのように ErrSyntax エラーを返さず、予期せぬ結果を返す可能性がありました。この挙動の不整合は、Goコンパイラと strconv パッケージの間で、同じ不正な入力に対する処理が異なるという問題を引き起こしていました。

この不整合は Issue 2658 として報告され、潜在的なセキュリティ上の懸念や、異なる処理系間での挙動の差異、あるいは開発者が期待するエラーハンドリングとの乖離につながる可能性がありました。このコミットは、この不整合を解消し、Goコンパイラと strconv パッケージの挙動を完全に一貫させることを目的としています。これにより、Goプログラム全体の堅牢性と予測可能性が向上します。

前提知識の解説

  • Go言語の文字列リテラルとエスケープシーケンス: Go言語では、ダブルクォート (") またはバッククォート (`) で囲まれた文字列リテラルを使用します。ダブルクォート文字列では、特定の文字を表現するためにバックスラッシュ (\) を使ったエスケープシーケンスが利用されます。

    • 8進数エスケープシーケンス (\ooo): バックスラッシュの後に最大3桁の8進数(0-7)を続けることで、その8進数に対応するバイト値を表現します。例: \101 は 'A' を表します。
    • 不正な8進数エスケープシーケンス: 8進数として有効でない数字(8や9)が続く場合、Goコンパイラはこれを不正なシーケンスとして扱い、コンパイルエラーを発生させます。
  • strconv パッケージ: Goの標準ライブラリの一部であり、文字列と基本的なデータ型(整数、浮動小数点数、真偽値など)の間で相互変換を行うための機能を提供します。例えば、文字列を整数に変換したり、整数を文字列に変換したりする際に使用されます。

  • strconv.UnquoteChar 関数: この関数は、引用符で囲まれた文字列(例: "abc\n")から、次のエスケープされた文字を読み取り、その文字のルーン値、それがマルチバイト文字であるか、残りの文字列、およびエラー情報を返します。文字列の解析において、エスケープシーケンスを適切に処理することが求められます。

  • ErrSyntax: strconv パッケージで定義されているエラー定数の一つです。入力文字列が期待される構文に合致しない場合、つまり、解析しようとしている文字列が不正な形式である場合に返されます。

  • Issue 2658: Goプロジェクトの公式バグトラッカー(GitHub Issues)で報告された特定のバグや機能改善要求を指します。このコミットは、このIssueで報告された問題を直接解決するために行われました。

技術的詳細

このコミットの核心は、strconv.UnquoteChar 関数が8進数エスケープシーケンスを解析する際のロジックの改善にあります。

UnquoteChar 関数は、入力文字列 s と引用符の種類 quote を受け取り、文字列の先頭から1文字をアンクォートします。8進数エスケープシーケンス (\ooo) の処理において、この関数はバックスラッシュの後に続く数字を読み取り、8進数として解釈します。

変更前は、src/pkg/strconv/quote.go 内の UnquoteChar 関数において、8進数エスケープシーケンスの数字を読み取るループ(for j := 0; j < 2; j++)内で、読み取った数字 x が8進数として有効な範囲(0から7)にない場合(if x < 0 || x > 7)のチェックは存在していました。しかし、このチェックが真となった場合でも、適切なエラー(ErrSyntax)を設定せずに return してしまうか、あるいは処理が続行されてしまう可能性がありました。これにより、\9 のような不正な8進数シーケンスが、エラーとして適切に扱われないという問題が発生していました。

このコミットでは、src/pkg/strconv/quote.goUnquoteChar 関数内の該当箇所に、err = ErrSyntax という1行が追加されました。この変更により、読み取った数字 x が8進数として有効な範囲(0から7)にない場合に、明示的に err 変数に ErrSyntax を設定し、直ちに return するように修正されました。これにより、不正な8進数エスケープシーケンスが検出された際に、必ず ErrSyntax が返されることが保証されます。

src/pkg/html/template/escape_test.go の変更は、この修正によって影響を受けたテストケースを示しています。以前は特定の「難読化された」エスケープシーケンス(例: e\78preS\0Sio)が、テンプレートエンジンによってブロックされ ZgotmplZ に置き換えられることを期待するテストがありました。しかし、UnquoteChar が不正な8進数シーケンスに対して ErrSyntax を返すようになったため、これらの難読化されたシーケンスはもはや有効な入力として扱われず、テンプレートエンジンが ZgotmplZ に置き換える必要がなくなりました。これは、セキュリティ上の脆弱性が新たに発生したわけではなく、むしろより厳密なエラーチェックが導入された結果として、テストの期待値が変わったことを意味します。これらのテストケースはコメントアウトされ、修正によってもはや必要ないか、あるいは新しい挙動に合わせて調整が必要であることが示されています。

最後に、src/pkg/strconv/quote_test.go には、\9, \19, \129 のような不正な8進数エスケープシーケンスを含む新しいテストケースが追加されました。これらのテストは、UnquoteChar 関数がこれらの不正な入力に対して正しく ErrSyntax を返すことを検証するために導入されました。

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

--- a/src/pkg/strconv/quote.go
+++ b/src/pkg/strconv/quote.go
@@ -260,6 +260,7 @@ func UnquoteChar(s string, quote byte) (value rune, multibyte bool, tail string,\
 		for j := 0; j < 2; j++ { // one digit already; two more
 			x := rune(s[j]) - '0'
 			if x < 0 || x > 7 {
+				err = ErrSyntax
 				return
 			}
 			v = (v << 3) | x

コアとなるコードの解説

この変更は、src/pkg/strconv/quote.go ファイル内の UnquoteChar 関数に位置しています。この関数は、文字列中のエスケープシーケンスを解析し、対応する文字を抽出する役割を担っています。

具体的には、上記の差分は、8進数エスケープシーケンス(例: \123)を処理する部分に焦点を当てています。このコードブロックは、バックスラッシュの後に続く数字を読み取り、それらを8進数として解釈して文字コードを構築するロジックの一部です。

変更前のコードでは、for j := 0; j < 2; j++ ループ内で、8進数の桁を読み取る際に x := rune(s[j]) - '0' を用いて文字を数値に変換し、if x < 0 || x > 7 という条件で、その数値が8進数として有効な範囲(0から7)にあるかをチェックしていました。しかし、このチェックが真(つまり、読み取った数字が8進数として不正)であった場合でも、err 変数に適切なエラーを設定せずに return してしまうか、あるいは処理が続行されてしまう可能性がありました。

追加された err = ErrSyntax の1行は、この問題を解決します。if x < 0 || x > 7 の条件が真になった場合、つまり不正な8進数(例: \99)が検出された場合に、直ちに err 変数に ErrSyntax を設定し、関数を終了させることを保証します。

この修正により、UnquoteChar 関数は、Goコンパイラが不正な8進数エスケープシーケンスを含む文字列リテラルに対して行うのと同様に、構文エラーを報告するようになります。これにより、strconv パッケージの堅牢性が向上し、Go言語全体のエスケープシーケンス処理における一貫性が確保されます。

関連リンク

参考にした情報源リンク