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

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

このコミットは、Go言語の標準ライブラリであるimage/jpegパッケージ内のJPEGデコーダが、JPEGストリーム中に存在する「フィルバイト (fill bytes)」を適切に処理するように修正するものです。具体的には、JPEGマーカーの前に挿入される可能性のある0xffバイトをスキップするロジックが追加されました。

コミット

commit 64b3e590c00b9c1e532dd0014b16174c9397664c
Author: Nigel Tao <nigeltao@golang.org>
Date:   Thu Nov 8 10:36:29 2012 +1100

    image/jpeg: handle fill bytes.
    
    Fixes #4337.
    
    R=r, minux.ma
    CC=golang-dev
    https://golang.org/cl/6814098

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

https://github.com/golang/go/commit/64b3e590c00b9c1e532dd0014b16174c9397664c

元コミット内容

image/jpeg: handle fill bytes. Fixes #4337.

このコミットは、Go言語のimage/jpegパッケージにおいて、JPEGデータストリーム内のフィルバイトを処理するように修正を加えるものです。これにより、GoのIssueトラッカーで報告されていた問題 #4337 が解決されます。

変更の背景

JPEGファイルフォーマットの仕様(特にITU-T Recommendation T.81 | ISO/IEC 10918-1)では、JPEGマーカーの前に任意の数の0xffバイト(フィルバイト)を挿入することが許可されています。これは、データストリームの同期やアライメントのために使用されることがあります。

Go言語のimage/jpegパッケージの既存の実装では、このフィルバイトの存在が考慮されていませんでした。そのため、一部のJPEGファイル、特にマーカーの前に0xffフィルバイトが含まれているファイルが正しくパースできず、エラーが発生する可能性がありました。この問題は、GoのIssue #4337として報告されており、このコミットはその問題を解決するために行われました。フィルバイトを適切にスキップすることで、より堅牢なJPEGデコーダが実現されます。

前提知識の解説

JPEGファイルフォーマットの基本

JPEG (Joint Photographic Experts Group) は、主に写真などの連続階調画像を圧縮するための標準的な画像フォーマットです。JPEGファイルは、複数の「セグメント」で構成されており、各セグメントは特定の情報(画像データ、メタデータ、圧縮パラメータなど)を含んでいます。

JPEGマーカー

JPEGファイル内の各セグメントは、2バイトの「マーカー」で始まります。マーカーは常に0xffバイトで始まり、その後にセグメントの種類を示す1バイトが続きます。例えば、0xffd8はSOI (Start Of Image) マーカー、0xffd9はEOI (End Of Image) マーカーです。デコーダはこれらのマーカーを読み取ることで、データストリーム内のセグメントの境界を識別し、適切に処理を進めます。

フィルバイト (Fill Bytes)

JPEG標準のセクションB.1.1.2には、「任意のマーカーは、オプションで任意の数のフィルバイト(コードX'FF'が割り当てられたバイト)によって先行されてもよい」と記載されています。これは、マーカーの前に0xffバイトが連続して出現する可能性があることを意味します。これらのフィルバイトは、データの内容には影響を与えず、デコーダはこれらを無視して次の有効なマーカーを探す必要があります。

Go言語のimage/jpegパッケージ

Go言語の標準ライブラリには、image/jpegパッケージが含まれており、JPEG画像のエンコードとデコードの機能を提供します。このパッケージは、image.Imageインターフェースを実装しており、他の画像フォーマット(PNG, GIFなど)と同様に統一された方法で画像を扱うことができます。内部的には、JPEGのデータストリームを読み込み、マーカーを解析し、圧縮された画像データをデコードしてピクセルデータに変換するロジックが含まれています。

技術的詳細

このコミットは、src/pkg/image/jpeg/reader.goファイル内のdecoder構造体のdecodeメソッドに修正を加えています。decodeメソッドは、io.ReaderからJPEGデータストリームを読み込み、画像をデコードする主要な関数です。

既存のコードでは、マーカーを読み取る際に、最初の0xffバイトの後に続く1バイトを直接マーカーとして解釈していました。しかし、JPEG標準で許可されているフィルバイトの存在を考慮していなかったため、0xffが連続して出現した場合に、最初の0xffをマーカーの開始バイトとして認識し、その次の0xffをマーカーの種類を示すバイトとして誤って解釈してしまう可能性がありました。これにより、不正なマーカーとしてエラーになったり、後続のパース処理が失敗したりする原因となっていました。

このコミットでは、マーカーを読み取る前に、連続する0xffバイトをスキップするためのforループが追加されました。このループは、d.r.ReadByte()を使用して1バイトずつ読み込み、読み込んだバイトが0xffである限りループを続けます。これにより、実際のマーカーバイト(0xffの後に続く、0xffではないバイト)が確実にmarker変数に格納されるようになります。

エラーハンドリングも適切に行われており、バイトの読み込み中にエラーが発生した場合は、そのエラーが呼び出し元に返されます。

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

変更はsrc/pkg/image/jpeg/reader.goファイルのdecode関数内、既存のマーカー読み取りロジックの直前に追加されています。

--- a/src/pkg/image/jpeg/reader.go
+++ b/src/pkg/image/jpeg/reader.go
@@ -236,6 +236,14 @@ func (d *decoder) decode(r io.Reader, configOnly bool) (image.Image, error) {
 		return nil, FormatError("missing 0xff marker start")
 	}
 	marker := d.tmp[1]
+	for marker == 0xff {
+		// Section B.1.1.2 says, "Any marker may optionally be preceded by any
+		// number of fill bytes, which are bytes assigned code X'FF'".
+		marker, err = d.r.ReadByte()
+		if err != nil {
+			return nil, err
+		}
+	}
 	if marker == eoiMarker { // End Of Image.
 		break
 	}

コアとなるコードの解説

追加されたコードブロックは以下の通りです。

	for marker == 0xff {
		// Section B.1.1.2 says, "Any marker may optionally be preceded by any
		// number of fill bytes, which are bytes assigned code X'FF'".
		marker, err = d.r.ReadByte()
		if err != nil {
			return nil, err
		}
	}
  1. for marker == 0xff: このループは、現在読み取られたmarkerバイトが0xffである限り継続します。初期のmarkerは、d.tmp[1]から取得されたもので、これは0xffマーカー開始バイトの直後に続くバイトです。もしこのバイトが0xffであれば、それはフィルバイトであると判断されます。
  2. marker, err = d.r.ReadByte(): d.rio.Readerインターフェースを実装しており、ここから次の1バイトを読み込みます。読み込んだバイトは新しいmarkerの値として格納されます。
  3. if err != nil: バイトの読み込み中にエラーが発生した場合(例: EOFに達した、I/Oエラーなど)、即座にエラーを返してデコード処理を中断します。これは堅牢なエラーハンドリングのために重要です。
  4. ループの継続: 新しく読み込んだmarkerが再び0xffであれば、ループは継続し、さらに次のバイトを読み込みます。これにより、連続するフィルバイトがすべてスキップされます。
  5. ループの終了: marker0xff以外の値になった時点でループは終了します。このときmarkerには、実際のJPEGマーカーの種類を示すバイトが格納されており、後続の処理で正しく解釈されるようになります。

この変更により、JPEGデコーダはJPEG標準に完全に準拠し、フィルバイトを含むJPEGファイルを問題なく処理できるようになりました。

関連リンク

  • Go言語のIssue #4337: このコミットが解決した具体的な問題に関する情報がGoのIssueトラッカーに存在します。
  • Go言語のimage/jpegパッケージのドキュメント: https://pkg.go.dev/image/jpeg
  • JPEG標準 (ITU-T Recommendation T.81 | ISO/IEC 10918-1): JPEGフォーマットの公式仕様書。フィルバイトに関する記述はセクションB.1.1.2にあります。

参考にした情報源リンク