[インデックス 19175] ファイルの概要
このコミットでは、src/pkg/encoding/base64/base64.go
と src/pkg/encoding/base64/base64_test.go
の2つのファイルが変更されています。base64.go
は Base64 エンコーディング/デコーディングの主要なロジックを含み、base64_test.go
はそのテストコードです。
コミット
commit a6d3cc2904e42338d31cca00024a153e7c1b502c
Author: Brad Fitzpatrick <bradfitz@golang.org>
Date: Wed Apr 16 11:32:41 2014 -0700
encoding/base64: don't lose a byte of output when encountering trailing garbage
Fixes #7733
LGTM=minux.ma
R=golang-codereviews, minux.ma
CC=golang-codereviews, nigeltao, r, rsc
https://golang.org/cl/88330044
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/a6d3cc2904e42338d31cca00024a153e7c1b502c
元コミット内容
encoding/base64: don't lose a byte of output when encountering trailing garbage
Fixes #7733
LGTM=minux.ma
R=golang-codereviews, minux.ma
CC=golang-codereviews, nigeltao, r, rsc
https://golang.org/cl/88330044
変更の背景
このコミットは、Go言語の encoding/base64
パッケージにおけるデコード処理のバグを修正するものです。具体的には、Base64エンコードされた入力文字列の末尾に「トレーリングガーベージ」(不正な文字)が存在する場合に、デコードされた出力の一部が失われる("lose a byte of output")という問題がありました。
従来のdecode
関数は、トレーリングガーベージを検出すると即座にCorruptInputError
を返していましたが、この際、エラーが検出される直前まで正しくデコードされていた有効なバイト数n
が正しく反映されない可能性がありました。つまり、エラーを返す時点で、既にデコードが完了していたはずの最後の有効なバイトが、出力としてカウントされないまま関数が終了してしまうという挙動です。
この問題は、Fixes #7733
として参照されているGoのIssueで報告されたものと考えられます。この修正により、不正な入力であっても、それまでに正しくデコードされた部分は確実に返されるようになり、デコーダの堅牢性と正確性が向上しました。
前提知識の解説
Base64エンコーディング
Base64は、バイナリデータをASCII文字列の形式に変換するエンコーディング方式です。主に、バイナリデータをテキストベースのプロトコル(例: 電子メール、HTTPなど)で安全に転送するために使用されます。
- 変換の基本: Base64は、3バイトのバイナリデータを4つのBase64文字(6ビットずつ)に変換します。
- 文字セット: 大文字A-Z、小文字a-z、数字0-9、
+
、/
の64文字を使用します。 - パディング (
=
): 入力データのバイト数が3の倍数でない場合、出力の最後にパディング文字=
が追加されます。例えば、1バイトのデータは==
、2バイトのデータは=
でパディングされます。
トレーリングガーベージ (Trailing Garbage)
Base64エンコードされた文字列の末尾に、有効なBase64文字(A-Z, a-z, 0-9, +, /)でもパディング文字(=)でもない余分な文字が存在する場合、これを「トレーリングガーベージ」と呼びます。Base64デコーダは、このような不正な文字を検出した場合、通常はエラーとして処理する必要があります。
CorruptInputError
encoding/base64
パッケージでは、入力されたBase64文字列が不正な形式である場合に CorruptInputError
というエラー型を返します。このエラーは、デコード処理中に不正な文字や不正なパディングが検出された際に発生します。
Go言語のエラーハンドリング
Go言語では、エラーは関数の戻り値として明示的に返されるのが一般的な慣習です。関数が正常に処理を完了した場合はnil
を返し、エラーが発生した場合はerror
インターフェースを満たす値を返します。呼び出し元は、戻り値のエラーをチェックすることで、処理の成功/失敗を判断します。
技術的詳細
このコミットの核心は、encoding/base64
パッケージの decode
メソッドにおけるエラー処理の変更です。
decode
メソッドは、Base64エンコードされたバイトスライス src
をデコードし、結果を dst
に書き込みます。この関数は、デコードされたバイト数 n
、入力の終端に達したかを示す end
、および発生したエラー err
の3つの値を返します。
元の実装では、デコードループの途中でトレーリングガーベージ(不正な文字)を検出した場合、以下のようなコードで即座にエラーを返していました。
// trailing garbage
return n, false, CorruptInputError(olen - len(src))
このreturn
文は、エラーが検出された時点でdecode
関数を即座に終了させます。問題は、このn
の値が、エラーが検出される直前まで正しくデコードされていたバイト数を正確に反映していない可能性があったことです。特に、有効なBase64シーケンスの直後に不正な文字が続く場合、不正な文字を検出した時点で即座にエラーを返していたため、その直前まで正しくデコードされていた最後のバイトがn
に含まれないまま返されてしまうというバグがありました。これにより、デコードされたはずのデータの一部が「失われる」という現象が発生していました。
このコミットによる修正は、この即座のreturn
を以下のように変更することで、この問題を解決しています。
err = CorruptInputError(olen - len(src))
これにより、トレーリングガーベージが検出されても、decode
関数は即座に終了せず、エラー情報をerr
変数に格納するだけになります。そして、デコードループが終了した後、最終的なreturn
文でn
, end
, err
の3つの値を返します。
// 修正前
return n, end, nil
// 修正後
return n, end, err
この変更により、エラーが検出された場合でも、それまでにデコードされた有効なバイト数n
が正しく計算され、最終的に返されるようになりました。つまり、入力が不正であっても、デコーダは可能な限り多くの有効なデータを抽出し、その上でエラーを報告する、より堅牢な挙動を示すようになります。
この修正を検証するために、base64_test.go
に TestDecoderIssue7733
という新しいテスト関数が追加されました。このテストは、"YWJjZA====="
という入力文字列を使用しています。この文字列は、"abcd"
をBase64エンコードした YWJjZA==
に、余分な =
が複数付加されたものです。
テストでは、この文字列をデコードした際に、以下の2つの条件が満たされることを検証しています。
- デコードされた文字列が
"abcd"
であること。これは、トレーリングガーベージが存在しても、有効な部分が正しくデコードされることを保証します。 - 返されるエラーが
CorruptInputError(8)
であること。これは、8バイト目(YWJjZA
の後の最初の=
)で不正な入力が検出されたことを示し、エラーが正しく報告されることを確認します。
このテストの追加により、修正が意図した通りに機能し、トレーリングガーベージが存在する場合でも有効な出力が失われないことが保証されました。
コアとなるコードの変更箇所
src/pkg/encoding/base64/base64.go
--- a/src/pkg/encoding/base64/base64.go
+++ b/src/pkg/encoding/base64/base64.go
@@ -250,7 +250,7 @@ func (enc *Encoding) decode(dst, src []byte) (n int, end bool, err error) {
}
if len(src) > 0 {
// trailing garbage
- return n, false, CorruptInputError(olen - len(src))
+ err = CorruptInputError(olen - len(src))
}
dlen, end = j, true
break
@@ -277,7 +277,7 @@ func (enc *Encoding) decode(dst, src []byte) (n int, end bool, err error) {
n += dlen - 1
}
- return n, end, nil
+ return n, end, err
}
// Decode decodes src using the encoding enc. It writes at most
src/pkg/encoding/base64/base64_test.go
--- a/src/pkg/encoding/base64/base64_test.go
+++ b/src/pkg/encoding/base64/base64_test.go
@@ -165,6 +166,7 @@ func TestDecodeCorrupt(t *testing.T) {
{"AAA=", -1},
{"AAAA", -1},
{"AAAAAA=", 7},
+ {"YWJjZA=====", 8},
}
for _, tc := range testCases {
dbuf := make([]byte, StdEncoding.DecodedLen(len(tc.input)))
@@ -329,3 +331,14 @@ bqbPb06551Y4
t.Error("Decoded results not equal")
}
}
+
+func TestDecoderIssue7733(t *testing.T) {
+ s, err := StdEncoding.DecodeString("YWJjZA=====")
+ want := CorruptInputError(8)
+ if !reflect.DeepEqual(want, err) {
+ t.Errorf("Error = %v; want CorruptInputError(8)")
+ }
+ if string(s) != "abcd" {
+ t.Errorf("DecodeString = %q; want abcd", s)
+ }
+}
コアとなるコードの解説
src/pkg/encoding/base64/base64.go
の変更点
-
エラーハンドリングの変更:
- 変更前:
return n, false, CorruptInputError(olen - len(src))
- 変更後:
err = CorruptInputError(olen - len(src))
この変更により、トレーリングガーベージが検出された際に、関数が即座に終了するのではなく、エラー情報をerr
変数に格納するようになりました。これにより、デコードループが最後まで実行され、n
(デコードされたバイト数)が正確に計算される機会が与えられます。
- 変更前:
-
最終的な戻り値の変更:
- 変更前:
return n, end, nil
- 変更後:
return n, end, err
デコードループの最後に、nil
ではなくerr
変数の内容を返すように変更されました。これにより、ループ内でCorruptInputError
が設定されていた場合、そのエラーが呼び出し元に正しく伝播されるようになります。この2つの変更が組み合わさることで、トレーリングガーベージが存在する場合でも、それまでにデコードされた有効なバイト数n
が正しく返され、かつエラーも適切に報告されるようになりました。
- 変更前:
src/pkg/encoding/base64/base64_test.go
の変更点
-
TestDecodeCorrupt
へのテストケース追加:{"YWJjZA=====", 8}
という新しいテストケースがtestCases
スライスに追加されました。"YWJjZA====="
は"abcd"
をBase64エンコードした文字列に余分なパディング文字が付加されたものです。このテストケースは、8バイト目(YWJjZA
の後の最初の=
)で不正な入力が検出されることを期待しています。これは、デコーダがトレーリングガーベージを正しく識別できることを確認するためのものです。
-
TestDecoderIssue7733
関数の追加:- この新しいテスト関数は、このコミットが修正する特定のシナリオを検証するために追加されました。
StdEncoding.DecodeString("YWJjZA=====")
を呼び出し、返される文字列s
とエラーerr
をチェックします。if !reflect.DeepEqual(want, err)
: 返されたエラーがCorruptInputError(8)
と等しいことを検証します。これは、エラーが正しく報告され、そのオフセットも正しいことを確認します。if string(s) != "abcd"
: デコードされた文字列が"abcd"
であることを検証します。これは、トレーリングガーベージが存在する場合でも、それまでにデコードされた有効な出力が失われないという、このコミットの主要な目的を直接的に確認するものです。
これらの変更により、encoding/base64
パッケージのデコーダは、不正な入力に対する挙動がより正確かつ堅牢になりました。
関連リンク
- Go CL (Change List):
https://golang.org/cl/88330044
(このCLは古いものであり、現在のGoのコードレビューシステムでは直接アクセスできない可能性があります。) - Go Issue #7733: このコミットが修正した問題のIssue番号です。コミットメッセージに記載されていますが、古いIssueのため、現在の公開Issueトラッカーで詳細を見つけるのは難しい場合があります。しかし、コミットメッセージ自体が問題の概要(トレーリングガーベージによる出力の損失)を明確に示しています。
参考にした情報源リンク
- コミットメッセージと差分 (
./commit_data/19175.txt
) - Base64エンコーディングに関する一般的な知識
- Go言語のエラーハンドリングに関する一般的な知識