モジュラープラグ370 NEW 発売予定 LANツール・部材 モジュラープラグ キットツール チェッカーLAN ツールLAN プラグモジュラー プラグカバーモジュラー
C言語静的解析ツールと Ruby 1.9 trunk
-
Upload
ikegami -
Category
Technology
-
view
2.896 -
download
0
description
Transcript of C言語静的解析ツールと Ruby 1.9 trunk
C言語静的解析ツールとRuby 1.9 trunk
池上 大介 @ikegami_ _
自己紹介
• 2003 誤り訂正符号で博士号
• 2003-2010 仕様記述やプログラムの静的検証に従事
• ツールを Haskell で作るのが仕事
• C 言語に対するツールを使うのは今回が初めて
• 10/27 - 11/10 の 2 週間の調査
• Ruby/Mathematica, Ruby/Ming, RushCheck, Karatsuba
@ikegami_ _
なにそれ楽しそう
匿名希望さんとのやりとり
結論 1/2• 既存の軽量でない C++ 検証ツール
• BLAST
• Frama-C
• 使いにくい
• ぼくがアホなだけ/検証項目を人間が書く
• ツールの要求するマシンパワーが足りない
• 現状の CIL の限界(GCC 拡張をパースできない)
結論 2/2• もっともな指摘
• 多すぎない警告
• cppcheck が妥当(C/C++ に対応)
• Emacs + Flymake
• Vim + ?
• Vim + QuickFix + errormaker
←二つを満たすのは難しい
セーブした瞬間にエラーがでる→嬉しい
Emacs + Flymake + cppcheck
静的解析ツール• 軽量:すばやく動く・簡単に使える
• cppcheck
• splint
• 軽量でない:遅い・マニュアル分厚い
• BLAST
• Frama-C
軽量静的解析とは• コンパイラの -Wall フラグみたいな
• ソースコードをパース
• ソースコードを理解
• 問題を発見
• しかるべきメッセージを出力
• すばやく解析
軽量でない静的解析ツール• division by zero
• ループを unroll
• if 分岐を非決定的実行
• 算術
• ポインタ解析
• Call flow graph
• 抽象化
• assert 文の解析
• コメントによる
assertion
あとで議論しましょう(?)
cppcheck
• 軽量 written in C++
• C/C++ を静的解析
• プリプロセス
• Tokenize
• Run all checks - pattern matching of the tokens
http://sourceforge.net/apps/trac/cppcheck/
cppcheck を ruby に
• ruby-1.9 trunk revision 33685 (2011-11-09 取得)
• compile.cを除く 77 files を 2:01:02.55 で解析
• error と指摘した 6 箇所
• compile.c を 54:46.94s で解析
• 仮想環境で走らせたので、本当はもっと速いはず
cppcheck を Ruby に
[hash.c:2351]: (error) Memory leak: str[io.c:5264]: (error) fflush() called on input stream "stdin" may result in undefined behaviour[regcomp.c:5524]: (error) Memory leak: new_reg[vm_dump.c:831]: (error) Possible null pointer dereference: vm - otherwise it is redundant to check if vm is null at line 778[vm_dump.c:834]: (error) Possible null pointer dereference: vm - otherwise it is redundant to check if vm is null at line 778[vm_dump.c:835]: (error) Possible null pointer dereference: vm - otherwise it is redundant to check if vm is null at line 778
6 箇所のエラー(?)
hash.c の該当箇所
2351 } /* 関数 ruby_setenv の終端 */
2303 str = malloc(len += strlen(value) + 2);
このあと str を free している形跡がない?
[hash.c:2351]: (error) Memory leak: str
2287 #elif defined __sun
Solaris 限定のメモリリークだった!
io.c の該当箇所
5264 fflush(stdin); /* is it really needed? */
[io.c:5264]: (error) fflush() called on input stream "stdin" may result in undefined behaviour
Q. How can I flush pending input so that a user's typeahead isn't read at the next prompt? Will fflush(stdin) work?A. fflush is defined only for output streams. (omit)
comp.lang.c FAQ list · Question 12.26a
splint• 軽量/軽量でない written in C
• 時間不足で調査が不十分(すみません)
• 自動検証 & コメントの annotation による検証
• cppcheck と比べて冗長な警告
• いくつかのファイルがパースできない
• cont.c gc.c random.c thread_pthread.h(ナンデ?)
http://www.splint.org/
splint hash.c
• ruby-1.9 trunk revision 33685 (2011-11-09 取得)
• 397 個の警告
• header をすべて(?)渡す必要がある
• Solaris のバグは Solaris 上の configure の結果をもらってこないといけない
• cppcheck が見つけた hash.c のメモリリークは x86 では見つからない
splint regcomp.c
• ruby-1.9 trunk revision 33685 (2011-11-09 取得)
• 737 個の警告
• 全部見て回るのは時間的に無理• 適当に拾った
splint regcomp.cregcomp.c:180:10: Only storage uslist->us->target (type struct _Node *) derived from released storage is not released (memory leak): uslist->us(omit)
176 static void 177 unset_addr_list_end(UnsetAddrList* uslist) 178 { 179 if (IS_NOT_NULL(uslist->us)) 180 xfree(uslist->us); 181 }
176 static void 177 unset_addr_list_end(UnsetAddrList* uslist) 178 { 179 if (IS_NOT_NULL(uslist->us)) 180 xfree(uslist->us); 181 } typedef struct {
int offset; struct _Node* target;} UnsetAddr;
typedef struct { int num; int alloc; UnsetAddr* us;} UnsetAddrList;
uslist->us->target の free を忘れている?
183 static int 184 unset_addr_list_add(UnsetAddrList* uslist, int offset, struct _Node* node) 185 { 186 UnsetAddr* p; 187 int size; 188 189 if (uslist->num >= uslist->alloc) { 190 size = uslist->alloc * 2; 191 p = (UnsetAddr* )xrealloc(uslist->us, sizeof(UnsetAddr) * size); 192 CHECK_NULL_RETURN_MEMERR(p); 193 uslist->alloc = size; 194 uslist->us = p; 195 } 196 197 uslist->us[uslist->num].offset = offset; 198 uslist->us[uslist->num].target = node; 199 uslist->num++; 200 return 0; 201 }
↑ free してはいけない(?)
静的解析はfalse positive との戦い
BLAST• with CIL (ビルドには OCaml が必要)
• 反例駆動モデル検査による安全性検証
• ユーザが assert() を書く
• プログラムを抽象モデルに自動変換
• assert に反するかどうかを判定
http://mtc.epfl.ch/software-tools/blast/index-epfl.php
escape 解析って何?
#include <assert.h>int watched; /* a global variable */void foo(int i) { watched = i; }void bar(){ int j;
foo(j); assert(j == watched); /* assert(j != watched); */}
% gcc -E -I ${BLAST_INCLUDE} -main bar target.c% pblast.opt target.i -main bar⇒「はい、代入されてます :-)」
←ここで代入される
#include <assert.h>int *watched;
void foo(int *p) { watched = p; }
void bar(){ int i, *j; i = 1; j = &i; foo(j); assert(j == watched); /* assert(j != watched); */}
ポインタ解析も可能
% gcc -E -I ${BLAST_INCLUDE} -main bar target.c% pblast.opt target.i -main bar⇒「はい、代入されてます :-)」
ruby 1.9 trunk に適用可能?
• for や while といったループ
• if の分岐
• などが解析不能になる原因
• やってみないとわからない世界
• 問題を人間が簡単にしてやる必要?
• 時間と手間と報われない労力
Frama-C• with CIL (ビルドには OCaml が必要)
• C の静的解析フレームワーク
• 解析プラグインの寄せ集め
• 軽量/軽量でない両方のプラグイン
• value plug-in
• users plug-in
http://frama-c.com/
←軽量静的解析
division by zerovoid foo(int x, int y){ int z = x / y; /* y should not be zero */ return;}
int main(int argc, char **argv){ int x = 1, y = 0; foo(x, y); return 0;}
Frama-C value plug-in
% frama-c -val foo.c[value] Analyzing a complete application starting at main【略】foo.c:3:[kernel] warning: division by zero: assert y ≢ 0;
division by zero• ruby trunk revision no. 33685
• bignum.c
• 1044 ds[k] = (BDIGIT)(num / hbase);
• util.c
• 数カ所に存在• 331 n = (r - l + size) / size;
Frama-C value pluginでは発見できず(時間切れ)
Frama-C users plug-in
• 軽量な callee の調査
• 関数間解析の基本
void foo(void) {}void bar(void) {foo();}
int main(void){ bar(); return 0;} % frama-c -users foo.c
[kernel] preprocessing with "gcc -C -E -I. foo.c"【中略】[users] ====== DISPLAYING USERS ====== bar: foo main: foo bar ====== END OF USERS ==========
ruby の string.c でもcallee 解析はできましたhttp://sovmoess.tumblr.com/post/12364993205/frama-c-ruby-1-9-string-c-callee
@ikegami_ _
静的解析の限界• 全自動軽量静的解析
• false positive との戦い
• 状態爆発するのでメモリが必要
• でかい構文木相手なので CPU も必要
• 手動 annotation による静的解析
• どこにどんな annotation を書くか?
• Frama-C + jessie plug-in → Coq で証明
まとめ再び• 2 週間の C 言語の静的解析ツール調査
• ruby-1.9 trunk revision 33685 に適用
• 軽量 cppcheck/splint は適用が簡単
• escape 解析は軽量静的解析では無理
• BLAST/Frama-C の適用は時間と考察が必要
• 労力が報われない可能性