29

C ++コンパイラは、メモリへの書き込みを最適化できます。

 {
     //all this code can be eliminated
     char buffer[size];
     std::fill_n( buffer, size, 0);
 }

機密データを処理する場合、一般的なアプローチは、volatile*ポインターを使用して、コンパイラーによってメモリー書き込みが確実に発行されるようにすることです。SecureZeroMemory()Visual C ++ランタイムライブラリ(WinNT.h)の関数は次のように実装されます。

FORCEINLINE PVOID RtlSecureZeroMemory(
     __in_bcount(cnt) PVOID ptr, __in SIZE_T cnt )
{
    volatile char *vptr = (volatile char *)ptr;
#if defined(_M_AMD64)
    __stosb((PBYTE )((DWORD64)vptr), 0, cnt);
#else
    while (cnt) {
        *vptr = 0;
        vptr++;
        cnt--;
    }
#endif
    return ptr;
}

この関数は、渡されたポインターをポインターにキャストしてから、volatile*ポインターを介して書き込みます。ただし、ローカル変数で使用する場合:

char buffer[size];
SecureZeroMemory( buffer, size );

変数自体はではありませんvolatile。したがって、C ++によると、監視可能な動作の標準定義では、書き込みは監視可能なbuffer動作としてカウントされず、最適化できるように見えます。

以下に、ページファイルやキャッシュなど、すべて有効なコメントがたくさんありますが、この質問では無視してみましょう。この質問に関する唯一のことは、メモリ書き込みのコードが最適化されているかどうかです。

メモリへの書き込みを行うコードがC++で最適化されていないことを確認することは可能ですか?ソリューションはSecureZeroMemory()C++標準に準拠していますか?

4

5 に答える 5

8

ポータブルなソリューションはありません。必要に応じて、コンパイラは、メモリ内の複数の場所でデータを使用しているときにデータのコピーを作成でき、ゼロ関数は、その時点で使用していたものだけをゼロにすることができます。どのソリューションも移植性がありません。

于 2012-11-08T09:18:31.390 に答える
4

のようなライブラリ関数を使用SecureZeroMemoryすると、ライブラリの作成者は通常、そのような関数がコンパイラによってインライン化されないようにするために苦労します。これは、スニペットで

char buffer[size];
SecureZeroMemory( buffer, size );

コンパイラは、が何をするのかSecureZeroMemoryを知らないbufferため、オプティマイザは、スニペットを取り出してもプログラムの監視可能な動作に影響がないことを証明できません。言い換えれば、ライブラリの作成者は、そのようなコードが最適化されないようにするために可能なすべてのことをすでに実行していることになります。

于 2012-11-08T08:41:07.380 に答える
2

キーワードは、キャストを必要とせずにポインター(またはC ++では参照)に適用できます。volatileつまり、このポインターを介したアクセスは最適化されません。変数の宣言は重要ではありません。

動作は次のようになりconstます。

char buffer[16];
char const *p = buffer;

buffer[0] = 'a';          // okay
p[0] = 'b';               // error

バッファへのconstポインタが存在することは、変数の動作を変更するのではなく、変更されたポインタの動作のみを変更します。変数が宣言されている場合、その変数への非ポインターをconst生成することは禁止されています。const

char const buffer[16];
char *p = buffer;         // error

同様に、

char buffer[16];
char volatile *p = buffer;

buffer[0] = 'a';          // may be optimized out
p[0] = 'b';               // will be emitted

char volatile buffer[16];
char *p = buffer;         // error

コンパイラーは、左辺値へのアクセスが発生しないことを証明できる関数呼び出しだけでなく、非volatile 左辺値を介したアクセスも自由に削除できvolatileます。

このRtlSecureZeroMemory関数は、コンパイラーが定義(volatileループ内のアクセス、またはプラットフォームによっては、コンパイラーに対して不透明で最適化できないと見なされるアセンブラー・ステートメントを含む)を参照できるため、安全に使用できます。関数がvolatileアクセスを実行すると想定します。

<winnt.h>ヘッダーファイルへの依存を避けたい場合は、同様の関数が準拠するコンパイラで正常に機能します。

于 2012-11-08T10:00:58.973 に答える
1

メモリに機密情報が存在するときとそれを消去するときの間には、常に競合状態があります。その時間枠内に、アプリケーションがクラッシュしてコアをダンプしたり、悪意のあるユーザーがプロセスのアドレス空間のメモリダンプを取得して、機密情報をプレーンテキストで送信したりする可能性があります。

機密情報をプレーンテキストでメモリに保存するべきではないかもしれません。このようにして、より良いセキュリティを実現し、この問題を完全に回避します。

于 2012-11-08T09:53:49.717 に答える
1

C標準もC++標準も、実装が物理メモリに物事を格納する方法に要件を課していません。ただし、実装はそのようなことを自由に指定できます。ただし、特定の物理メモリの動作を必要とするアプリケーションに適した高品質の実装では、適切な方法で一貫して動作することが指定されます。

多くの実装では、少なくとも2つの異なる方言が処理されます。「最適化が無効になっている」方言を処理するとき、物理メモリと相互作用するアクションの数を詳細に文書化することがよくあります。残念ながら、最適化を有効にすると、通常、意味的に弱い方言に切り替わり、アクションが物理メモリとどのように相互作用するかについてほとんど何も保証されません。問題になる可能性が高い特定の簡単に識別できるケースでは、「最適化が無効」の方言と一致する方法で処理しながら、多くの単純で直接的な最適化を処理できるはずですが、コンパイラの作成者は関心がありません。安全でぶら下がっている果物に焦点を当てたモードを提供します。

物理的記憶が特定の方法で処理されることを保証する唯一の信頼できる方法は、物理的記憶をその方法で処理することを約束する方言を使用することです。そうすれば、必要な治療を受けるのは一般的に簡単です。そうでなければ、「創造的な」実装が予期しないことをしないことを保証するものは何もありません。

于 2018-07-30T16:12:23.113 に答える