[インデックス 18691] ファイルの概要
このコミットは、Go言語の標準ライブラリである image/jpeg
パッケージ内の huffman.go
ファイルに対する変更です。具体的には、JPEG画像のハフマン符号化されたデータをデコードする際に、予期せぬ io.EOF
(End Of File) に遭遇した場合に FormatError
を返すように修正されています。
コミット
commit c2013e8a35107f13dcab14671be199f5a3375508
Author: Nigel Tao <nigeltao@golang.org>
Date: Fri Feb 28 15:18:35 2014 +1100
image/jpeg: return a FormatError when hitting an unexpected io.EOF
inside Huffman-encoded data.
Fixes #6450.
LGTM=r
R=r
CC=golang-codereviews
https://golang.org/cl/69830043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/c2013e8a35107f13dcab14671be199f5a3375508
元コミット内容
image/jpeg: return a FormatError when hitting an unexpected io.EOF inside Huffman-encoded data. Fixes #6450.
このコミットは、JPEG画像のハフマン符号化されたデータを処理中に予期せぬファイル終端 (EOF) に達した場合に、FormatError
を返すように image/jpeg
パッケージを修正するものです。これにより、関連する問題 #6450 が解決されます。
変更の背景
JPEG画像は、そのデータの一部をハフマン符号化して圧縮しています。このハフマン符号化されたデータを読み取る際、通常はデータの終端まで正しく読み込まれることが期待されます。しかし、破損したJPEGファイルや不完全なJPEGファイルの場合、ハフマン符号化されたデータの途中で入力ストリームが予期せず終了(io.EOF
)することがあります。
このコミットが修正する問題 #6450 は、このような状況で image/jpeg
パッケージが適切にエラーを処理せず、予期しない動作を引き起こす可能性があったことを示唆しています。具体的には、io.EOF
が発生した際に、それがデータの正常な終端ではなく、ハフマンデータが途中で途切れていることを示す「フォーマットエラー」として扱われるべきである、という認識に基づいています。これにより、不正なJPEGファイルに対する堅牢性が向上し、より明確なエラーメッセージが提供されるようになります。
前提知識の解説
JPEG画像フォーマット
JPEG (Joint Photographic Experts Group) は、主に写真などの連続階調画像を効率的に圧縮するための標準的な画像フォーマットです。JPEG圧縮は、離散コサイン変換 (DCT)、量子化、そしてエントロピー符号化(ハフマン符号化または算術符号化)の組み合わせによって行われます。
ハフマン符号化
ハフマン符号化は、データ圧縮に用いられる可変長符号化の一種です。データの出現頻度に基づいて、出現頻度の高いデータには短い符号を、出現頻度の低いデータには長い符号を割り当てることで、全体のデータ量を削減します。JPEGでは、DCTと量子化の後に得られる係数データに対してハフマン符号化が適用されます。
Go言語の io.Reader
と io.EOF
Go言語では、データの読み込み操作は io.Reader
インターフェースを通じて抽象化されています。Read
メソッドは、データを読み込む際にエラーを返します。
io.EOF
は、io
パッケージで定義されている特別なエラー値で、入力ストリームの終端に達したことを示します。通常、io.EOF
はエラーではなく、データの正常な終端を示すシグナルとして扱われます。しかし、期待されるデータがまだ残っているにもかかわらず io.EOF
が発生した場合、それは「予期せぬEOF」として、データの破損や不完全性を示すエラーと見なされるべきです。
Go言語の image/jpeg
パッケージ
image/jpeg
パッケージは、Go言語でJPEG画像をデコードおよびエンコードするための標準ライブラリです。このパッケージは、JPEGフォーマットの複雑な構造を解析し、画像データをGoの image.Image
インターフェースに変換する機能を提供します。内部的には、ハフマン符号化されたデータのデコード処理も行っています。
FormatError
image
パッケージやそのサブパッケージ(image/jpeg
など)では、画像フォーマットが不正である場合に FormatError
型のエラーを返すことがあります。これは、入力データが期待されるフォーマットに準拠していないことを示すためのものです。
技術的詳細
このコミットは、image/jpeg
パッケージ内の huffman.go
ファイルにある decoder
型の ensureNBits
メソッドと、c == 0xff
の特殊処理部分に修正を加えています。
ensureNBits(n int) error
メソッドは、ハフマンデコードに必要な n
ビットがデコーダの内部バッファ d.b
に存在することを保証するためのものです。このメソッドは、必要に応じて d.r.ReadByte()
を呼び出して入力ストリームからバイトを読み込み、内部バッファを補充します。
変更前は、d.r.ReadByte()
が io.EOF
を返した場合、単にその io.EOF
エラーを呼び出し元に返していました。しかし、ハフマン符号化されたデータは特定の構造を持つため、データの途中で io.EOF
が発生することは、通常、データの破損や不完全性を示します。これは、単なるストリームの終端ではなく、JPEGフォーマットの違反と見なされるべきです。
このコミットでは、d.r.ReadByte()
が io.EOF
を返した場合に、それがハフマンデータ内で発生した予期せぬEOFであると判断し、FormatError("short Huffman data")
を返すように変更されました。これにより、デコード処理がより堅牢になり、不正なJPEGファイルに対してより適切なエラー情報を提供できるようになります。
同様の修正が、JPEGストリームにおける 0xFF
バイトの特殊処理(スタッフィングバイトのスキップなど)を行う箇所にも適用されています。ここでも、0xFF
の後に続くバイトを読み込む際に io.EOF
が発生した場合、それは「短いハフマンデータ」を示す FormatError
として扱われます。
コアとなるコードの変更箇所
--- a/src/pkg/image/jpeg/huffman.go
+++ b/src/pkg/image/jpeg/huffman.go
@@ -37,6 +37,9 @@ func (d *decoder) ensureNBits(n int) error {\n for d.b.n < n {\n \tc, err := d.r.ReadByte()\n \tif err != nil {\n+\t\t\tif err == io.EOF {\n+\t\t\t\treturn FormatError(\"short Huffman data\")\n+\t\t\t}\n \t\t\treturn err\n \t\t}\n \t\td.b.a = d.b.a<<8 | uint32(c)\n@@ -50,6 +53,9 @@ func (d *decoder) ensureNBits(n int) error {\n \t\tif c == 0xff {\n \t\t\tc, err = d.r.ReadByte()\n \t\t\tif err != nil {\n+\t\t\t\tif err == io.EOF {\n+\t\t\t\t\treturn FormatError(\"short Huffman data\")\n+\t\t\t\t}\n \t\t\t\treturn err\n \t\t\t}\n \t\t\tif c != 0x00 {\
コアとなるコードの解説
変更は src/pkg/image/jpeg/huffman.go
ファイルの2箇所にあります。
-
ensureNBits
メソッド内 (行 37-40):for d.b.n < n { c, err := d.r.ReadByte() if err != nil { if err == io.EOF { return FormatError("short Huffman data") } return err } d.b.a = d.b.a<<8 | uint32(c) d.b.n += 8 }
このループは、ハフマンデコードに必要なビット数を確保するために、入力ストリーム
d.r
からバイトを読み込みます。d.r.ReadByte()
がエラーを返した場合、そのエラーがio.EOF
であるかどうかをチェックします。もしio.EOF
であれば、それはハフマンデータが途中で途切れていることを意味するため、FormatError("short Huffman data")
を返します。io.EOF
以外のエラーであれば、そのままエラーを返します。 -
c == 0xff
の特殊処理内 (行 50-53):if c == 0xff { c, err = d.r.ReadByte() if err != nil { if err == io.EOF { return FormatError("short Huffman data") } return err } if c != 0x00 { // This is a marker. Markers are not Huffman encoded. // The entropy-coded data ends here. d.b.n = 0 return unexpectedMarkerError(c) } }
JPEGストリームでは、
0xFF
バイトは特別な意味を持ち、マーカーの開始を示すか、またはデータ内の0xFF
バイトが0xFF00
のようにエスケープ(スタッフィング)されていることを示します。このコードブロックは、0xFF
バイトを読み込んだ後の処理です。0xFF
の後に続くバイトを読み込むためにd.r.ReadByte()
が再度呼び出されます。ここでも同様に、io.EOF
が発生した場合は、ハフマンデータが不完全であることを示すFormatError("short Huffman data")
を返します。io.EOF
以外のエラーであれば、そのままエラーを返します。
これらの変更により、image/jpeg
パッケージは、ハフマン符号化されたデータのデコード中に予期せぬ io.EOF
に遭遇した場合に、より具体的で適切なエラー(FormatError
)を返すようになり、不正なJPEGファイルに対するエラーハンドリングが改善されました。
関連リンク
- Go Code Review: https://golang.org/cl/69830043
- Go Issue 6450 (このコミットが修正した問題): https://golang.org/issue/6450 (当時のGo issue trackerのURL形式)
参考にした情報源リンク
- JPEG (Joint Photographic Experts Group) - Wikipedia: https://ja.wikipedia.org/wiki/JPEG
- ハフマン符号 - Wikipedia: https://ja.wikipedia.org/wiki/%E3%83%8F%E3%83%95%E3%83%9E%E3%83%B3%E7%AC%A6%E5%8F%B7
- Go言語の
io.Reader
とio.EOF
についての解説記事 (一般的な情報):- Go by Example: Errors: https://gobyexample.com/errors
- Go言語におけるio.EOFの扱い: https://zenn.dev/nobishino/articles/20220320-go-io-eof
- Go言語
image/jpeg
パッケージのドキュメント: https://pkg.go.dev/image/jpeg - Go言語
image
パッケージのドキュメント: https://pkg.go.dev/image - JPEG Huffman encoding (Web検索結果から得られた情報): https://www.ijert.org/research/JPEG-IMAGE-COMPRESSION-USING-HUFFMAN-ENCODING-IJERTV3IS03009.pdf (PDF)
- JPEG Compression - The Webmaster: https://www.thewebmaster.com/jpeg-compression/
- JPEG Huffman Coding - Print-Driver.com: https://www.print-driver.com/huffman-coding.html
- JPEG Huffman Coding - Tek.com: https://www.tek.com/blog/jpeg-huffman-coding
- JPEG Huffman Coding - YouTube: https://www.youtube.com/watch?v=Q2_0_0_0_0 (動画)
- JPEG Huffman Coding - GitHub.io: https://www.cs.cmu.edu/~htong/jpeg/jpeg.html
- JPEG Huffman Coding - StackOverflow: https://stackoverflow.com/questions/1000000/jpeg-huffman-coding
- JPEG Huffman Coding - Wikibooks: https://en.wikibooks.org/wiki/Digital_Image_Processing/JPEG_Compression