コンパイラ、discover、および uncover は、コード内の静的コードの問題、動的メモリーアクセスの問題、およびカバレージの問題を検出します。この付録では、これらのツールで検出され、コードアナライザで分析される特有のエラーの種類について説明します。
コードカバレージ検査では、カバーされていない関数が特定されます。結果では、見つかったコードカバレージの問題に「カバーされていない関数」というラベルが付けられ、潜在的なカバレージの割合が示されます。この割合は、該当する関数をカバーするテストを追加した場合にアプリケーションの合計カバレージが何パーセント増加するかを示しています。
考えられる原因: 関数を実行するテストが行われなかったか、デッドコードまたは古いコードを削除していません。
ABR: 配列境界を越える読み取り (beyond array bounds read)
ABW: 配列境界を越える書き込み (beyond array bounds write)
DFM: メモリーの二重解放 (double freeing memory)
ECV: 明示的型キャスト違反 (explicit type cast violation)
FMR: 解放済みメモリーの読み取り (freed memory read)
FMW: 解放済みメモリーの書き込み (freed memory write)
INF: 空の無限ループ (infinite empty loop)
MLK: メモリーリーク (memory leak)
MFR: 関数の復帰なし (missing function return)
MRC: malloc 戻り値の検査なし (missing malloc return value check)
NFR: 初期化されていない関数の復帰 (uninitialized function return)
NUL: NULL ポインタ間接参照、リークの可能性があるポインタの検査
RFM: 解放済みメモリーを返す (return freed memory)
UMR: 初期化されていないメモリーの読み取り、初期化されていないメモリーの読み取りビット操作 (uninitialized memory read, uninitialized memory read bit operation)
URV: 使用されていない戻り値 (unused return value)
VES: スコープ外での局所変数の使用 (out-of-scope local variable usage)
このセクションでは、エラーの考えられる原因と、エラーが発生する可能性があるコードの例を説明します。
考えられる原因: 配列境界を越えてメモリーを読み取ろうとしました。
例:
int a[5];
. . .
printf("a[5] = %d\n",a[5]); // Reading memory beyond array bounds
考えられる原因: 配列境界を越えてメモリーに書き込もうとしました。
例:
int a [5];
. . .
a[5] = 5; // Writing to memory beyond array bounds
考えられる原因: 同じポインタを使用して free()() を複数回呼び出しました。C++ では、同じポインタに対して delete 演算子を 2 回以上使用しています。
例:
int *p = (int*) malloc(sizeof(int));
free(p);
. . . // p was not signed a new value between the free statements
free(p); // Double freeing memory
例:
int *p = (int*) malloc(sizeof(int));
free(p);
. . . // Nothing assigned to p in between
printf("p = 0x%h\n",p); // Reading from freed memory
例:
int *p = (int*) malloc(sizeof(int));
free(p);
. . . // Nothing assigned to p in between
*p = 1; // Writing to freed memory
例:
int x=0;
int i=0;
while (i200) {
x++; } // Infinite loop
考えられる原因: メモリーが割り当てられるが、関数の終了またはエスケープの前に解放されていません。
例:
int foo()
{
int *p = (int*) malloc(sizeof(int));
if (x) {
p = (int *) malloc(5*sizeof(int)); // will cause a leak of the 1st malloc
}
} // The 2nd malloc leaked here
考えられる原因: 終了するパスの一部に戻り値がありません。
例:
#include <stdio.h>
int foo (int a, int b)
{
if (a)
{
return b;
}
} // If foo returns here, the return is uninitialized
int main ( )
{
printf("%d\n", foo(0,30));
}
考えられる原因: Cの malloc または C++ の new 演算子からの戻り値に null 検査なしでアクセスします。
例:
#include <stdlib.h>
int main()
{
int *p3 = (int*) malloc(sizeof(int)); // Missing null-pointer check after malloc.
*p3 = 0;
}
考えられる原因: null に等しくなる可能性のあるポインタにアクセスしているか、null になることのないポインタに冗長な null 検査を行なっています。
例:
#include <stdio.h>
#include <stdlib.h>
int gp, ctl;
int main()
{
int *p = gp;
if (ctl)
p = 0;
printf ("%c\n", *p); // May be null pointer dereference
if (!p)
*p = 0; // Surely null pointer dereference
int *p2 = gp;
*p2 = 0; // Access before checking against NULL.
assert (p2!=0);
int *p3 = gp;
if (p3) {
printf ("p3 is not zero.\n");
}
*p3 = 0; // Access is not protected by previous check against NULL.
}
例:
#include <stdlib.h>
int *foo ()
{
int *p = (int*) malloc(sizeof(int));
free(p);
return p; // Return freed memory is dangerous
}
int main()
{
int *p = foo();
*p = 0;
}
考えられる原因: 初期化されていないローカルデータまたはヒープデータの読み取り。
例:
#include <stdio.h>
#include <stdlib.h>
struct ttt {
int a: 1;
int b: 1;
};
int main()
{
int *p = (int*) malloc(sizeof(int));
printf("*p = %d\n",*p); // Accessing uninitialized data
struct ttt t;
extern void foo (struct ttt *);
t.a = 1;
foo (&t); // Access uninitialized bitfield data "t.b"
}
考えられる原因: 初期化されていないローカルデータまたはヒープデータの読み取り。
例:
int foo();
int main()
{
foo(); // Return value is not used.
}
考えられる原因: 初期化されていないローカルデータまたはヒープデータの読み取り。
例:
int main()
{
int *p = (int *)0;
void bar (int *);
{
int a[10];
p = a;
} // local variable 'a' leaked out
bar(p);
}
動的メモリーアクセス検査では、次の種類のエラーが検出されます。
ABR: 配列境界を越える読み取り (beyond array bounds read)
ABW: 配列境界を越える書き込み (beyond array bounds write)
BFM: 不正な空きメモリー (bad free memory)
BRP: 不正な realloc アドレスパラメータ (bad realloc address parameter)
CGB: 破損したガードブロック (corrupted guard block)
DFM: メモリーの二重解放 (double freeing memory)
FMR: 解放済みメモリーの読み取り (freed memory read)
FMW: 解放済みメモリーの書き込み (freed memory write)
FRP: 解放済み realloc パラメータ (freed realloc parameter)
IMR: 無効なメモリーの読み取り (invalid memory read)
IMW: 無効なメモリーの書き込み (invalid memory write)
MLK: メモリーリーク (memory leak)
OLP: 送り側と受け側の重複 (overlapping source and destination)
PIR: 部分的に初期化された読み取り (partially initialized read)
SBR: スタック境界を越える読み取り (beyond stack bounds read)
SBW: スタック境界を越える書き込み (beyond stack bounds write)
UAR: 割り当てられていないメモリーの読み取り (unallocated memory read)
UAW: 割り当てられていないメモリーの書き込み (unallocated memory write)
UMR: 初期化されていないメモリーの読み取り (uninitialized memory read)
このセクションでは、エラーの考えられる原因と、エラーが発生する可能性があるコードの例を説明します。
考えられる原因: 配列境界を越えてメモリーを読み取ろうとしました。
例:
int a[5];
. . .
printf("a[5] = %d\n",a[5]); // Reading memory beyond array bounds
考えられる原因: 配列境界を越えてメモリーに書き込もうとしました。
例:
int a [5];
. . .
a[5] = 5; // Writing to memory beyond array bounds
考えられる原因: free()() または realloc()() にヒープデータ以外のポインタを渡しました。
例:
#include <stdlib.h>
int main()
{
int *p = (int*) malloc(sizeof(int));
free(p+1); // Freeing wrong memory block
}
例:
#include <stdlib.h>
int main()
{
int *p = (int*) realloc(0,sizeof(int));
int *q = (int*) realloc(p+20,sizeof(int[2])); // Bad address parameter for realloc
}
考えられる原因: 動的に割り当てられた配列の末尾を越えて「レッドゾーン」に書き込んでいます。
例:
#include <stdio.h>
#include <stdlib.h>
int main() {
int *p = (int *) malloc(sizeof(int)*4);
*(p+5) = 10; // Corrupted array guard block detected (only when the code is not annotated)
free(p);
return 0;
}
考えられる原因: 同じポインタを使用して free()() を複数回呼び出しました。C++ では、同じポインタに対して delete 演算子を 2 回以上使用しています。
例:
int *p = (int*) malloc(sizeof(int));
free(p);
. . . // p was not assigned a new value between the free statements
free(p); // Double freeing memory
例:
int *p = (int*) malloc(sizeof(int));
free(p);
. . . // Nothing assigned to p in between
printf("p = 0x%h\n",p); // Reading from freed memory
例:
int *p = (int*) malloc(sizeof(int));
free(p);
. . . // Nothing assigned to p in between
*p = 1; // Writing to freed memory
例:
#include <stdlib.h>
int main() {
int *p = (int *) malloc(sizeof(int));
free(0);
int *q = (int*) realloc(p,sizeof(it[2])); //Freed pointer passed to realloc
}
考えられる原因: ハーフワード境界、ワード境界、またはダブルワード境界に整列していないアドレスから、それぞれ 2、4、または 8 バイトを読み取っています。
例:
#include <stdlib.h>
int main()
{
int *p = 0;
int i = *p; // Read from invalid memory address
}
考えられる原因: ハーフワード境界、ワード境界、またはダブルワード境界に整列していないアドレスに、それぞれ 2、4、または 8 バイトを書き込んでいます。テキストアドレスに書き込んでいるか、読み取り専用データセクション (.rodata) に書き込んでいるか、mmap によって読み取り専用にされているページに書き込んでいます。
例:
int main()
{
int *p = 0;
*p = 1; // Write to invalid memory address
}
考えられる原因: メモリーが割り当てられるが、関数の終了またはエスケープの前に解放されていません。
例:
int foo()
{
int *p = (int*) malloc(sizeof(int));
if (x) {
p = (int *) malloc(5*sizeof(int)); // will cause a leak of the 1st malloc
}
} // The 2nd malloc leaked here
考えられる原因: 正しくないソース、宛先、または長さが指定されました。ソースと宛先が重複している場合、プログラムの動作は不定になります。
例:
#include <stlib.h>
#include <string.h>
int main() {
char *s=(char *) malloc(15);
memset(s, 'x', 15);
memcpy(s, s+5, 10);
return 0;
}
例:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int *p = (int*) malloc(sizeof(int));
*((char*)p) = 'c';
printf("*(p = %d\n",*(p+1)); // Accessing partially initialized data
}
考えられる原因: ローカル配列の末尾よりあと、または先頭より前を読み取っています。
例:
#include <stdio.h>
int main() {
int a[2] = {0, 1};
printf("a[-10]=%d\n",a[-10]); // Read is beyond stack frame bounds
return 0;
}
考えられる原因: ローカル配列の末尾よりあと、または先頭より前に書き込んでいます。
例:
#include <stdio.h>
int main() {
int a[2] = {0, 1};
a[-10] = 2; // Write is beyond stack frame bounds
return 0;
}
考えられる原因: ストレイポインタ (不正な値を持つポインタ)、ヒープブロック境界のオーバーフロー、すでに解放されたヒープブロックへのアクセス。
例:
#include <stdio.h>
#include <stdlib>
int main()
{
int *p = (int*) malloc(sizeof(int));
printf("*(p+1) = %d\n",*(p+1)); // Reading from unallocated memory
}
考えられる原因: ストレイポインタ (不正な値を持つポインタ)、ヒープブロック境界のオーバーフロー、すでに解放されたヒープブロックへのアクセス。
例:
#include <stdio.h>
#include <stdlib>
int main()
{
int *p = (int*) malloc(sizeof(int));
*(p+1) = 1; // Writing to unallocated memory
}
考えられる原因: 初期化されていないローカルデータまたはヒープデータの読み取り。
例:
#include <stdio.h>
#include <stdlib>
int main()
{
int *p = (int*) malloc(sizeof(int));
printf("*p = %d\n",*p); // Accessing uninitialized data
}
動的メモリーアクセス検査では、次の種類の警告が検出されます。
AZS: 0 サイズの割り当て (allocating zero size)
メモリーリーク
SMR: 投機的な非初期化メモリーからの読み取り (speculative uninitialized memory read)
このセクションでは、警告の考えられる原因と、警告が発生する可能性があるコードの例を説明します。
例:
#include <stdlib>
int main()
{
int *p = malloc(); // Allocating zero size memory block
}
考えられる原因: メモリーが割り当てられるが、関数の終了またはエスケープの前に解放されていません。
例:
int foo()
{
int *p = (int*) malloc(sizeof(int));
if (x) {
p = (int *) malloc(5*sizeof(int)); // will cause a leak of the 1st malloc
}
} // The 2nd malloc leaked here
例:
int i;
if (foo(&i) != 0) /* foo returns nonzero if it has initialized i */
printf("5d\n", i);
コンパイラは、前述のソースに対して次の同等のコードを生成する可能性があります。
int i;
int t1, t2'
t1 = foo(&i);
t2 = i; /* value in i is loaded. So even if t1 is 0, we have uninitialized read due to speculative load */
if (t1 != 0)
printf("%d\n", t2);