UNINIT.HEAP.MUST
初期化されていないヒープの使用
UNINIT.HEAP.MUST チェッカーは、使用前に初期化されていなかった malloc で割り当てられるヒープメモリを検出します。
脆弱性とリスク
初期化されていないヒープメモリを使用すると、コードのパフォーマンスが大幅に不安定になる可能性があります。これは、所定のデータオブジェクトに割り当てられた値が、割り当てられたヒープメモリから無作為に選択され、以前に使用されたオブジェクトや、別のプロセスからのオブジェクトのステートを反映する可能性があるためです。ソフトウェアによりメモリが正しく初期化されない場合は、予期しない結果が発生し、セキュリティに影響を及ぼす可能性があります。
軽減と防止
ヒープメモリは、よく使用される割り当て関数 malloc() から返された直後は、常に初期化されていない状態です。初期化されていないメモリの問題を回避するには、最初に使用する前に、すべての変数およびリソースが明示的に初期化されていることを確認します。
または、割り当て済みメモリをすべてゼロに自動的に初期化する、標準の calloc() ライブラリ関数を使用します。
脆弱コード例 1
#include<stdlib.h>
typedef struct {
int x;
} S1;
typedef struct {
S1* ptr;
} S2;
void foo(S1* ptr) {
int k = ptr ? ptr->x : -1;
}
int main() {
S2* ps = (S2*)malloc(sizeof(S2));
if( ps != NULL ) {
foo(ps->ptr);
free(ps);
}
return 0;
}
Klocwork は、変数 'ps->ptr' が、18 行目で初期化されないまま使用されることを示す 18 行目にフラグを立てます。この変数は、16 行目で割り当てられたメモリから値を取得します。
修正コード例 1
#include<stdlib.h>
typedef struct {
int x;
} S1;
typedef struct {
S1* ptr;
} S2;
void foo(S1* ptr) {
int k = ptr ? ptr->x : -1;
}
int main() {
S2* ps = (S2*)calloc(1, sizeof(S2));
if( ps != NULL ) {
foo(ps->ptr);
free(ps);
}
return 0;
}
修正コードでは、malloc() の呼び出しを同様の calloc() 呼び出しに単純に置換して、割り当てられたメモリが確実に事前にゼロに初期化されるようにしました。これにより、ポインター 'ps' を使用する前に、'S2' 構造体の各要素を手動で初期化する必要がなくなります。
脆弱コード例 2
#include<stdlib.h>
typedef struct {
int x, y;
} S1;
int foo(S1* ptr) {
return ptr->x * ptr->y;
}
int main() {
S1* ps = (S1*)malloc(sizeof(S1));
if( ps != NULL ) {
foo(ps);
free(ps);
}
}
Klocwork は、12 行目で割り当てられるメモリが、初期化されないまま 14 行目で使用されることを示す 14 行目にフラグを立てます。
修正コード例 2
#include<stdlib.h>
typedef struct {
int x, y;
} S1;
int foo(S1* ptr) {
return ptr->x * ptr->y;
}
int main() {
S1* ps = (S1*)malloc(sizeof(S1));
if( ps != NULL ) {
ps->x = 32, ps->y = 64;
foo(ps);
free(ps);
}
}
修正コードでは、calloc() 関数を使用してメモリを割り当てる代わりに、構造体 'S1' のメンバーを明示的に初期化しました。これにより、値がポインター 'ps' から読み取られる時点で、有効であることが既に知られています。