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

同カテゴリーの次の記事

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

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


17. プライベート・データのクリアには専用の関数を使用する

Apache HTTP Server* プロジェクトから抜粋した以下のコードについて考えてみます。このエラーは、次の PVS-Studio 診断によって検出されます。

V597 The compiler could delete the ‘memset’ function call, which is used to flush ‘x’ buffer. The RtlSecureZeroMemory() function should be used to erase the private data. (V597 コンパイラーは、’x’ バッファーのフラッシュに使用される ‘memset’ 呼び出しを削除できます。プライベート・データの消去には、RtlSecureZeroMemory() 関数を使用すべきです。)

static void MD4Transform(
  apr_uint32_t state[4], const unsigned char block[64])
{
  apr_uint32_t a = state[0], b = state[1],
               c = state[2], d = state[3],
               x[APR_MD4_DIGESTSIZE];  
  ....
  /* Zeroize sensitive information. */
  memset(x, 0, sizeof(x));
}

説明

このコードでは、プログラマーはプライベート・データの消去に memset() 関数呼び出しを使用しています。しかし、この方法では実際にデータが消去されないため、これは最良の方法ではありません。正確には、消去されるかどうかはコンパイラーやその設定などに依存します。

このコードをコンパイラーの視点から見てみましょう。コンパイラーは、コードをできるだけ高速に実行できるように、さまざまな最適化を行います。その 1 つは、C/C++ 言語の観点から余分なプログラムの動作に影響しない関数呼び出しを削除します。上記のコード例では、memset() 関数がこれに当たります。この関数は、’x’ バッファーを変更しますが、このバッファーは以降の処理で使用されません。つまり、memset() 関数呼び出しは削除可能であり、そうすべきです。

重要! これから述べることは、実際のコンパイラーの動作であって、理論モデルではありません。上記の例では、コンパイラーは memset() 関数呼び出しを削除します。これは、いくつかの実験をすることで確認できます。この問題の詳細と例は、次の記事を参照してください。

  1. セキュリティー、セキュリティー! しかし、それをテストしていますか? (英語)
  2. プライベート・データの安全な消去 (英語)
  3. V597 (英語) The compiler could delete the ‘memset’ function call, which is used to flush ‘Foo’ buffer. The RtlSecureZeroMemory() function should be used to erase the private data. (V597 コンパイラーは、’Foo’ バッファーのフラッシュに使用される ‘memset’ 呼び出しを削除できます。プライベート・データの消去には、RtlSecureZeroMemory() 関数を使用すべきです。)
  4. ゼロにして忘れる — C でメモリーをゼロにする場合の警告 (英語) (この記事に関する議論 (英語) も参照)
  5. MSC06-C. Beware of compiler optimizations. (MSC06-C. コンパイラーの最適化に気を付ける)
    https://www.securecoding.cert.org/confluence/display/c/MSC06-C.+Beware+of+compiler+optimizations

memset() 呼び出しの削除に関するこのエラーは、追跡が非常に困難なため特に厄介です。デバッガーを使用する場合、プログラマーはおそらく最適化前のまだ関数呼び出しが含まれているコードで作業します。しかし、このエラーは、最適化されたバージョンのビルド時に生成されるアセンブラー・リストを確認して初めて見つけることができます。

一部のプログラマーは、これはコンパイラーのバグであり、memset() のような重要な関数の呼び出しを削除すべきではないと考えています。しかし、それは違います。この関数がほかの関数よりも重要であるということはなく、コンパイラーはその呼び出しを最適化する権利があります。事実、そのようなコードが実際に余分である場合があります。

正しいコード

memset_s(x, sizeof(x), 0, sizeof(x));

または

RtlSecureZeroMemory(x, sizeof(x));

推奨事項

コンパイラーが最適化で削除できない特別なメモリー消去関数を使用すべきです。

例えば、Visual Studio* の RtlSecureZeroMemory (英語) 関数や C11 の memset_s (英語) があります。必要に応じて、独自の安全な関数を作成することもできます。インターネットには、さまざまな例があります。そのいくつかを以下に示します。

バージョン No.1 (https://www.securecoding.cert.org/confluence/display/c/MSC06-C.+Beware+of+compiler+optimizations):

errno_t memset_s(void *v, rsize_t smax, int c, rsize_t n) {
  if (v == NULL) return EINVAL;
  if (smax > RSIZE_MAX) return EINVAL;
  if (n > smax) return EINVAL;
  volatile unsigned char *p = v;
  while (smax-- && n--) {
    *p++ = c;
  }
  return 0;
}

バージョン No.2 (英語):

void secure_zero(void *s, size_t n)
{
    volatile char *p = s;
    while (n--) *p++ = 0;
}

一部のプログラマーは、さらに配列に疑似乱数値を設定する関数を実装して、これらの関数を異なる時点で実行することで時間計測攻撃からの保護を確実にしています。これらの関数の実装についても、インターネットで見つけることができます。

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

関連記事

  • Parallel Universe マガジンParallel Universe マガジン Parallel Universe へようこそ。 米国インテル社が四半期に一度オンラインで公開しているオンラインマガジンです。インテルの技術者によるテクノロジーの解説や、最新ツールの紹介など、並列化に関する記事を毎号掲載しています。第1号からのバックナンバーを PDF 形式で用意しました、ぜひご覧ください。 12 […]
  • 比較関数の罠比較関数の罠 この記事は、インテル® デベロッパー・ゾーンに公開されている「The Evil within the Comparison Functions」の日本語参考訳です。 この記事の PDF […]
  • ポピュラーなオープンソース・プロジェクトのエラーを見つけるクイズに挑戦しようポピュラーなオープンソース・プロジェクトのエラーを見つけるクイズに挑戦しよう この記事は、インテル® デベロッパー・ゾーンに公開されている「Let's Play a Game - find bugs in popular open-source projects」の日本語参考訳です。 PVS-Studio スタティック・コード・アナライザーの開発者達よって、プログラマー向けに C/C++ […]
  • ファクトシート: oneAPIファクトシート: oneAPI この記事は、インテル ニュースルームに公開されている「Fact Sheet: oneAPI」の日本語参考訳です。 この記事の PDF 版はこちらからご利用になれます。 oneAPI とは? oneAPI […]
  • マルチスレッド開発ガイド: 4.4(a) インテル® Inspector を使用したマルチスレッド・コードのデータ競合の検出マルチスレッド開発ガイド: 4.4(a) インテル® Inspector を使用したマルチスレッド・コードのデータ競合の検出 この記事は、インテル® ソフトウェア・ネットワークに掲載されている「Using Intel® Inspector XE 2011 to Find Data Races in Multithreaded Code」の日本語参考訳です。 編集注記: 本記事は、2012 年 2 月 24 […]