今回は、第2回でも取り上げたオープンソースの画像処理ライブラリであるlibtiffに最近見つかった脆弱性を、セキュリティコードレビューを行いながら見てみましょう。
本稿のアップデート版が執筆者のサイトで掲載されています。併せてご参照ください。
サンプルコード
以下のコードはlibtiff 3.9.2の/libtiff/tif_getimage.cから抜粋したものです。実際のコードでは、マクロを何段にも活用して関数定義を行っています。ここでは、YCbCrtoRGB以外のマクロはすべて展開した形で引用しました。それでは、このコードのどこに問題があるのかを考えてみましょう。
/* * YCbCr -> RGB conversion and packing routines. */ #define YCbCrtoRGB(dst, Y) { \ uint32 r, g, b; \ TIFFYCbCrtoRGB(img->ycbcr, (Y), Cb, Cr, &r, &g, &b); \ dst = ((uint32)(r)|((uint32)(g)<<8)|((uint32)(b)<<16)|(((uint32)0xffL)<<24)) \ } /* * 8-bit packed YCbCr samples w/ 2,2 subsampling => RGB */ static void putcontig8bitYCbCr22tile( TIFFRGBAImage* img, uint32* cp, uint32 x, uint32 y, uint32 w, uint32 h, int32 fromskew, int32 toskew, unsigned char* pp ) { uint32* cp2; (void) y; fromskew = (fromskew / 2) * 6; cp2 = cp+w+toskew; while (h>=2) { x = w; while (x>=2) { uint32 Cb = pp[4]; uint32 Cr = pp[5]; YCbCrtoRGB(cp[0], pp[0]); YCbCrtoRGB(cp[1], pp[1]); YCbCrtoRGB(cp2[0], pp[2]); YCbCrtoRGB(cp2[1], pp[3]); cp += 2; cp2 += 2; pp += 6; x -= 2; } if (x==1) { uint32 Cb = pp[4]; uint32 Cr = pp[5]; YCbCrtoRGB(cp[0], pp[0]); YCbCrtoRGB(cp2[0], pp[2]); cp ++ ; cp2 ++ ; pp += 6; } cp += toskew*2+w; cp2 += toskew*2+w; pp += fromskew; h-=2; } (中略) }
putcontig8bitYCbCr22tile()関数は画像データの変換に使われる関数のひとつです。libtiffライブラリ内部では、画像データを1ピクセルRGBA各8ビット合計32ビットで表現しています。入出力においてさまざまな形式の画像データに対応するため、このような変換関数が多数用意されているのです。
元データは引数に渡されるimgの先にあり、変換したデータを格納するための十分な大きさを持ったバッファはあらかじめ用意され、引数のcpで渡されます。fromskewやtoskewは画像の反転などのような変形操作のパラメータです。whileループのなかでは、各ピクセルデータを指すポインタを動かしながら、各ピクセルの色情報をTIFFYCbCrtoRGB()を使ってYCbCr形式からRGB形式に変換してバッファに書き込んでいくという処理を行っています。
脆弱性の解説:64ビット環境で問題になる整数変換
このコードで注目すべき箇所は、ポインタを移動させる
cp += toskew*2 + w;
という演算です。ここで使われている各変数の型を確認してみてください。
ポインタ値 += 2 * int32値 + uint32値
このコードが64ビット環境でコンパイル・実行されると、int32型の変数値が負の値をとるときに問題が発生します。それでは順を追って見ていきましょう。
代入式の右辺をみると、(int32型 * 定数) + uint32型となっており、これはint32型 + uint32型の演算式として評価されます。int32型は符号付き整数型、uint32型は符号無し整数型なので、これら2つのオペランドは異なる型を持ちます。このように異なる型が混在する加算演算を行う場合にはどちらか一方の型に合わせるような型変換が行われます。さて、どちらの型に合わせるか分かりますか? Cでは「通常の算術型変換」(usual arithmetic conversion)と呼ばれるルールに従い、型変換が行われます。