メインコンテンツ

不確かなメモリのクリーン アップ

コードでは機密である可能性がある情報をメモリからクリアするが、コンパイラ最適化では、その情報が消去されずに残される可能性がある

R2022a 以降

説明

この欠陥は、0 をメモリに書き込むことによってメモリをクリーン アップしたのに、クリーン アップ操作の後にメモリが読み取られない場合に発生します。クリーン アップ後にメモリが読み取られないため、コンパイラがコードを最適化する際にクリーン アップ操作をスキップする可能性があります。たとえば、次のコードを考えます。

void foo(){
	void* buffer = getSensitiveData();
	//...
	memset(buffer, 0, sizeof(buffer));
}
ここでは、buffer に機密データが含まれる可能性があります。memset() の呼び出しは、データをスクラブするために行われます。スクラブ後にバッファーが読み取られないため、コンパイラ最適化プロセスで memset() コマンドが無視される可能性があります。その場合、buffer 内のデータはクリーン アップされずに残されます。Polyspace® は、このような不確かなメモリのクリーン アップにフラグを設定します。以下のいずれかの条件に該当する場合、Polyspace はこの欠陥を報告しません。

  • メモリのクリーン アップ関数の戻り値が変数に代入される。

  • メモリのクリーン アップ関数がグローバル オブジェクトをスクラブする。

リスク

変数が書き込まれた後に読み取られることがないと、コンパイラがこれをデッドストアと見なす可能性があります。コンパイラはコードを最適化するために、デッドストア変数へのすべての代入を削除する可能性があります。

このようなコード最適化によって、メモリの機密情報をスクラブするコードが削除された場合、機密情報がクリーン アップされずにシステム内に残る可能性があります。攻撃者がこの機密情報にアクセスし、これを使用してコード内の保護メカニズムをさらに侵害する恐れがあります。

修正方法

メモリをスクラブするときは、コンパイラが削除できないコードを使用します。たとえば、memset ではなく、memset_sfill_n などの安全な関数を使用します。これらの関数を使用できない古いバージョンの C または C++ を使用している場合は、コードをより新しいバージョンに更新することを検討してください。あるいは、安全な独自のメモリ スクラブ関数を実装することもできます。独自の実装を使用する場合は、コンパイルされたコードをレビューして、コンパイラがメモリ クリーン アップ操作に干渉しないことを確認してください。

すべて展開する

#include <string.h>

extern int getSensitiveData(char*, size_t);
extern int writeDB(char*, char*);
char Gbuffer[64];
void Job(char *key) {
    char buffer[64];
    if (getSensitiveData(buffer, sizeof(buffer))) {
        if (writeDB(key, buffer)) {
            // Record data
        }
    }
    (void) memset(buffer, 0, sizeof(buffer)); // Defect
}

void manageGBuffer(char *key) {
    //...
    memset(Gbuffer, 0, sizeof(Gbuffer)); // No Defect
}

この例では、Job() でコマンド memset() を使用して、buffer 内のメモリをクリーン アップしています。クリーン アップ後に buffer が読み取られないため、コンパイラがコードを最適化する際に、このコードを削除する可能性があります。Polyspace はこの不確かなクリーン アップにフラグを設定します。関数 manage() では、memset() を使用してグローバル配列 Gbuffer をクリーン アップしています。このクリーン アップ操作には、Polyspace はフラグを設定しません。

修正 — 安全な関数を使用して機密情報が含まれるメモリをクリーン アップする

この欠陥を修正するには、memset_s() などの安全な保護された関数を使用して不確かなメモリをクリーン アップします。memset_s() を使用する前に、マクロ __STDC_WANT_LIB_EXT1__ 1 を定義します。詳細は、memset_s() を参照してください。

#define __STDC_WANT_LIB_EXT1__ 1
#include <string.h>
#define SIZE 64
extern int getSensitiveData(char *, size_t);
extern int writeDB(char *, char *);
char Gbuffer[SIZE];
void Job(char *key) {
	char buffer[64];
	if(getSensitiveData(buffer, sizeof(buffer))) {
		if(writeDB(key, buffer)) {
			// Record data
		}
	}
	#ifdef __STDC_LIB_EXT1__
	(void) memset_s(buffer, SIZE, 0, sizeof(buffer)); // Defect
	#endif
}

void manageGBuffer(char *key) {
	//...
	memset(Gbuffer, 0, sizeof(Gbuffer)); // No Defect
}

結果情報

グループ: セキュリティ
言語: C | C++
既定値: オフ
コマンド ライン構文: UNCERTAIN_MEMORY_CLEANING
影響度: Medium

バージョン履歴

R2022a で導入