はじめに
この連載では、最初に問題のあるコードを示します。前回に引き続き今回も、まずはコードだけを見て、どこに問題があるのか考えてみてください。
コードの後には、コードに含まれる脆弱性を見つけるためのヒントや、コードが行おうとしていることを理解するために役立つ背景知識などを説明します。コードを見ただけではどこに問題があるのか分からない、といった場合は、これらの説明を手がかりに考えてみてください。
どこに問題があるのか分かったら、次にどのように修正すべきかを考えましょう。修正方法は一通りとは限りません。むしろ、複数の修正方法が考えられることが多いと思います。
最後に、実際にどのような修正が行われたか説明します。自分が考えた修正案と比較してみてください。
サンプルコード
さて、皆さんにコードレビューしていただくのは、TIFFファイルの読み書きを行うためのライブラリであるlibTIFF v3.6.1です。libTIFFはUNIXやLinux系OS、Windows、OpenVMSなどさまざまなプラットフォームで利用できる、可搬性の高いオープンソースのライブラリです。さまざまなアプリケーションに利用されています。
以下がその脆弱なコードです。どのような問題がこのコードにあるのか考えてみましょう。
/* Fetch a tag that is not handled by special case code. */ static int TIFFFetchNormalTag(TIFF* tif, TIFFDirEntry* dp) { ... if (dp->tdir_count > 1) { /* array of values */ char *p = NULL; switch (dp->tdir_type) { ... case TIFF_ASCII: case TIFF_UNDEFINED: /* bit of a cheat ... */ /* * Some vendors write strings w/o the trailing * NULL byte, so always append one just in case. */
cp = CheckMalloc(tif, dp->tdir_count+1, 1, mesg); if ( (ok = (cp && TIFFFetchString(tif, dp, cp))) != 0 ) cp[dp->tdir_count] = '\0'; /* XXX */ break; } ... static char * CheckMalloc(TIFF* tif, tsize_t n, const char* what) { char *cp = (char*)_TIFFmalloc(n); if (cp == NULL) TIFFError(tif->tif_name, "No space %s", what); return (cp); }
TIFFとlibTIFF
脆弱性は、libtiff version 3.6.1のIFDのIFD Entryを処理する関数TIFFFetchNormalTagに存在しました。問題の解説を始める前にまず、TIFFのファイルフォーマットについて簡単に説明します。
TIFF(Tagged Image File Format)は1986年にMicrosoftとAldus(後にAdobeが買収)が策定した画像ファイルフォーマットの1つです。TIFFには、解像度や符号化方式が異なる複数の画像データを1つのファイルにまとめて格納できるという特徴があり、特定のアプリケーションに依存しないため、さまざまなプログラムや組込み機器がTIFFをサポートしています。
TIFFファイルの先頭8バイトはイメージファイルヘッダ(Image File Header)と呼ばれ、これはIFD(Image File Directory)を参照します。TIFFファイルには複数の画像を格納できるので、その場合は画像ごとにIFDエントリが存在することになります。各IFDエントリには、画像に関するデータと実画像データへのポインタが含まれます(図1)。