iCodeCheckerためしてみた

参考サイト：http://ttanimu.wordpress.com/2012/05/09/icodechecker/
参考サイト：http://www.ipa.go.jp/security/vuln/iCodeChecker/index.html

IPAから提供してもらっているiCodeChecker。本当に教育用でした。

ソースチェックの制限内容：

+ 存在しないヘッダファイルをinclude しているソースコードは検査不可。
+ コンパイルエラーが発生するソースコードは検査不可。
+ goto 文を使用しているソースコードは検査不可。<br>ラベルによるジャンプを検出した時点で検査終了。
+ 本ツールで検査可能なソースコードは、C 言語（C89）。<br>**C++のソースコードは検査不可**。
+ **構造体を利用しているソースコードは検査不可**。<br>構造体のメンバアクセスを検出した時点で検査終了。

試す前にこの後ろ２つの制限を知ってればインストールしてなかったよorz

----

試した環境：CentOS 6.2

```c:test/main.c
#include <stdio.h>
#include <string.h>
#include "sub.h"

void main(void)
{
        int i,j,k;
        unsigned int x,y;
        char b1[16], b2[32], *b3;

        i=sizeof(b1);
        x=sizeof(b2);

        memcpy(b1, b2, x);
        memcpy(b2, b1, i);

        j=sizeof(b3);
        if ( x != y )
                k=add(x,y);

        y=100000;
        b1[y]=y;
}
```

```c:test/sub.h
int add(int, int);
```

```c:test/sub.c
int add(int a, int b)
{
        return( a + b );
}
```

そして念のためにコンパイルが通ることを確認してから、
チェック実施
`gcc -g test/main.c test/sub.c`
`export LD_LIBRARY_PATH=/usr:/usr/local/lib:/usr/local/lib/boost/1_46_1/stage/lib:${LD_LIBRARY_PATH}`
`/usr/local/iCodeChecker/ichecker test`

以下結果

```標準出力
指定されたファイルは非対応の形式です。(エラーコード:-1030)
指定されたファイルは非対応の形式です。(エラーコード:-1030)
ソースコードセキュリティ検査が完了しました。
4個の脆弱なコードが見つかりました。
脆弱性一覧を以下に出力しました。
/home/～/index.txt
```

```index.txt
No., 脆弱性が検出されたファイル名, 行数, 脆弱性名, レポートファイル名
1, main.c, 14, CWE-120 入力サイズ未チェックでのバッファコピー（バッファオーバーフローの問題）, main.c_14_1.txt
2, main.c, 17, CWE-467 ポインタに対する sizeof 演算子の使用, main.c_17_2.txt
3, main.c, 18, CWE-457 未初期化変数の利用, main.c_18_3.txt
4, main.c, 22, CWE-129 配列インデックスの検証の不備, main.c_22_4.txt
```

```main.c_14_1.txt
【脆弱性名】
CWE-120 入力サイズ未チェックでのバッファコピー（バッファオーバーフローの問題）

【問題が発見されたファイル名と行数】
main.c:14
(/home/～/test/main.c)

【問題検出箇所】
11:     i=sizeof(b1);
12:     x=sizeof(b2);
13:
14:     memcpy(b1, b2, x);
15:     memcpy(b2, b1, i);
16:
17:     j=sizeof(b3);


【問題の原因】
入力サイズ未チェックで、バッファコピーが行われています。
バッファコピーを行う際に、コピー元のデータサイズに対して、コピー先のバッファサイズが十分なサイズであるか検証せずにコピーを行っています。

【脅威】
バッファコピーにおいて、コピー元のデータサイズがコピー先のバッファサイズよりも大きい場合、バッファオーバーフローが発生する可能性があります。
本問題が悪用されると、メモリ上の大事なデータを上書きされてしまい、
プログラムを異常終了されたり、外部から任意のコードを実行されたりする可能性があります。

【修正方法及び修正例】
バッファコピーを行う際は、コピー元のデータサイズに対してコピー先のバッファサイズが十分なサイズであるか検証する必要があります。また、コピーサイズを指定できない関数の利用は避け、コピーサイズが指定可能な関数を利用するようにしましょう。
次に問題のあるコードの例を示します。

 1: int func(char* data, size_t size)
 2: {
 3:     char   buf[64] = { '\0' };
 4:     size_t len = (size_t)0;
 5:
 6:     len = strlen(data) / 3 * 2;
 7:
 8:     strncpy(buf, data, len);
 9:     buf[len] = '\0';
10:     ...
11: }

上記のソースコードは、固定長の配列変数 buf に関数 func の引数として渡された変数 data の前半 2/3 のデータを書き込みます。コピーサイズの指定が可能な strncpy 関数を利用してバッファコピーを行っていますが(8行目)、指定するコピーサイズである変数 len と変数 buf のサイズの比較検証がされていません。そのため data のサイズが 96 バイトを超える場合、バッファオーバーフローが発生します。
本問題の修正方法としては次の 1 点があげられます。(1) を必ず実施するようにしましょう。

(1) 境界検査を行う
コピー先のバッファサイズがコピー元のデータを収めるのに十分に大きいことを検証しましょう。

次に、上記の方法で問題を修正したコードを示します。

 1: int func(char* data, size_t size)
 2: {
 3:     char   buf[64] = { '\0' };
 4:     size_t len = (size_t)0;
 5:
 6:     len = strlen(data) / 3 * 2;
 7:
 8:     /* 境界検査を行う */
 9:     if( len < sizeof(buf) )
10:     {
11:         /* コピーサイズを指定できる関数を利用する */
12:         strncpy(buf, data, len);
13:         buf[len] = '\0';
14:     }
15:     ...
16: }

修正例では、バッファコピーを行う前にコピーするデータサイズ(変数 len)が変数 buf のバッファサイズより小さいことを検証し(9 行目)、変数 buf のバッファサイズよりコピーするデータサイズが大きい場合はコピーを実施しません。また、コピーの後、コピー先バッファをヌル文字('\0')で終端しています(13行目)。

【参考情報】
セキュア・プログラミング講座　第10章 著名な脆弱性対策　バッファオーバーフロー: #2 ソースコード記述時の対策
http://www.ipa.go.jp/security/awareness/vendor/programmingv2/contents/c902.html

CERT セキュアコーディングスタンダード ARR33-C. コピーは必ず十分なサイズの記憶領域に対して行われることを保証する
http://www.jpcert.or.jp/sc-rules/c-arr33-c.html

AppGoat(アーカイブソフトの異常終了), (FTPプロキシソフトの異常終了（スタック領域）)
http://www.ipa.go.jp/security/vuln/appgoat/

CWE-120: Buffer Copy without Checking Size of Input ('Classic Buffer Overflow')
http://cwe.mitre.org/data/definitions/120.html
```

```main.c_18_3.txt
【脆弱性名】
CWE-457 未初期化変数の利用

【問題が発見されたファイル名と行数】
main.c:18
(/home/～/test/main.c)

【問題検出箇所】
15:     memcpy(b2, b1, i);
16:
17:     j=sizeof(b3);
18:     if ( x != y )
19:             k=add(x,y);
20:
21:     y=100000;


【問題の原因】
未初期化の変数を比較演算の引数として利用しています。
明示的に初期化されていないローカル変数の内容は、不定となります。

【脅威】
明示的に初期化されていないローカル変数を比較演算の引数として利用していた場合、その後の処理が開発者の想定していないものになってしまう可能性があります。
その結果、バッファオーバーフローなどが発生する原因となる可能性があります。
本問題が悪用されると、場合によってはメモリ上の大事なデータを上書きされてしまい、プログラムを異常終了されたり、外部から任意のコードを実行されたりする可能性があります。

【修正方法及び修正例】
初期化前の変数を利用しないようにしましょう。
次に問題のあるコードの例を示します。

 1: int func()
 2: {
 3:     char    src[]   = "abc";
 4:     char    dst[10] = { '\0' };
 5:     size_t  src_len = sizeof(src);
 6:     size_t  dst_len;
 7:
 8:     if(src_len <= dst_len)
 9:     {
10:         strncpy(dst, src, dst_len);
11:     }
12:     ...
13:
14:     return 0;
15: }

上記のソースコードは、コピー元のデータサイズであるローカル変数 src_len とコピー先のバッファサイズローカル変数 dst_len との検証を行うことで、バッファオーバーフローへの対策を実施しています(8行目)。しかし、ローカル変数dst_len は初期化せずに利 用しているため、その内容は不定なものとなっています。
その結果、メモリの状況によってはローカル変数dst_len の内容が巨大な数値となってしまい、この検証は正常に機能しない場合があります。そのため、strncpy 関数において、想定しないサイズのバッファコピーが発生してしまう可能性があります(10行目)。
本問題の修正方法としては次の 1 点があげられます。(1)を必ず実施するようにしましょう。

(1) 初期化前の変数を利用しない
変数を用いる前には、必ず初期化を行い、内容が不定なものとなっている未初期化変数を評価などに用いないようにしましょう。

次に、上記の方法で問題を修正したコードを示します。

 1: int func()
 2: {
 3:     char    src[]   = "abc";
 4:     char    dst[10] = { '\0' };
 5:     size_t  src_len = sizeof(src);
 6:     /* 変数の初期化を行う */
 7:     size_t  dst_len = sizeof(dst);
 8:
 9:     if(src_len <= dst_len)
10:     {
11:         strncpy(dst, src, dst_len);
12:     }
13:     ...
14:
15:     return 0;
16: }

修正例では、変数 dst_len を変数宣言時に初期化する(7行目)ことによって、変数 dst_len の内容が不定なまま利用されることがな いようにしています。

【参考情報】
CERT セキュアコーディングスタンダード EXP33-C. 未初期化のメモリを参照しない
http://www.jpcert.or.jp/sc-rules/c-exp33-c.html

CWE-457: Use of Uninitialized Variable
http://cwe.mitre.org/data/definitions/457.html
```

他にもmain.c_17_2.txt、main.c_21_4.txtがあるけど似たような文なので割愛させていただきました。
詳細の方はもうほぼ定型文でうめつくされるので正直一回見ておけばもう用は無いだろう。
１８行目の未初期化変数利用の問題の件だが、１９行目も似たような記述をしているのに
こちらは検知してくれない。。。。
ユーザ・マニュアルによると、

>9.7. CWE-457 未初期化変数利用の問題
>問題の原因
>未初期化の変数を**比較演算の引数**として利用しています。

そう。比較していないから、検知できないっぽい。。。。( ﾟдﾟ)ﾎﾟｶｰﾝ............

配列のサイズを追っかけるとか、かなりすごいことやってる割には、
テストする項目が限定的すぎ＆構造体が使用不可なために
本当に初心者教育用のソースコードチェックにしか使えない代物でした。

でも、iCodeCheckerのベースに使用しているROSE Compilerはかなり検知できそうなので
その存在をインストール中に知ることができただけでも頑張ってソースコードからインストールした甲斐がありました。