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

[インデックス 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.Readerio.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箇所にあります。

  1. 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 以外のエラーであれば、そのままエラーを返します。

  2. 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ファイルに対するエラーハンドリングが改善されました。

関連リンク

参考にした情報源リンク