プログラミング、リファクタリング、そしてすべてにおける究極の疑問: No. 32

その他インテル® DPC++/C++ コンパイラー特集

この記事は、インテル® デベロッパー・ゾーンに公開されている「The Ultimate Question of Programming, Refactoring, and Everything」の日本語参考訳です。


32. 危険な printf

TortoiseGIT プロジェクトから抜粋した以下のコードについて考えてみます。このコードにはエラーが含まれています。PVS-Studio アナライザーは、次の診断を出力します。

V618 It’s dangerous to call the ‘printf’ function in such a manner, as the line being passed could contain format specification. The example of the safe code: printf(“%s”, str); (V618 渡される行に書式設定を含めることができるため、このような方法で ‘printf’ 関数を呼び出すことは危険です。安全なコードの例: printf(“%s”, str);)

BOOL CPOFile::ParseFile(....)
{
  ....
  printf(File.getloc().name().c_str());
  ....
}

説明

文字列を出力したり、ファイルへ書き込む場合、多くのプログラマーは次のようなコードを記述します。

printf(str);
fprintf(file, str);

これは非常に危険であることを忘れてはいけません。何らかの形で書式指定子が文字列内に入ってしまうと、予期しない結果になります。

オリジナルのコード例に戻りましょう。ファイル名が “file%s%i%s.txt” の場合、プログラムはクラッシュするか、無意味なものを出力する可能性があります。しかし、それは問題の半分に過ぎません。実際、このような関数呼び出しは脆弱性につながります。利用することで、プログラムを攻撃できます。特別な方法で文字列を準備して、メモリーに格納されているプライベート・データを出力することが可能です。

この脆弱性に関する詳細は、こちらの記事 (英語) を参照してください。興味深い内容なのでお読みになると良いでしょう。理論的な根拠だけでなく、実例も示されています。

正しいコード

printf("%s", File.getloc().name().c_str());

推奨事項

printf() のような関数は、多くのセキュリティー関連の問題を引き起こす可能性があります。それらは使用せずに、より現代的なものに切り替えたほうが良いでしょう。例えば、boost::formatstd::stringstream は非常に便利です。

一般に、printf()sprintf()fprintf() などの関数の不注意な使用は、プログラムの不正な動作を引き起こすだけでなく、第三者が利用可能な潜在的な脆弱性につながる可能性があります。

コンパイラーの最適化に関する詳細は、最適化に関する注意事項を参照してください。

タイトルとURLをコピーしました