Slide 1

Slide 1 text

C言語プログラムの構造と ほんの少し解釈 mumumu 西村啓佑 ( @mumumu_vm ) Kernel/VM探検隊online part3

Slide 2

Slide 2 text

背景:大規模なコードのメンテは難しい Linuxでの実例:kmalloc()のメモリ確保とmemset()の ゼロクリアをkzalloc()に置換したい.どうする? fh = kmalloc(sizeof(struct zoran_fh), GFP_KERNEL); if (!fh) { dprintk(1,KERN_ERR"%s: zoran_open(): … ¥n", ZR_DEVNAME(zr)); return -ENOMEM; } memset(fh, 0, sizeof(struct zoran_fh)); fh = kzalloc(sizeof(struct zoran_fh), GFP_KERNEL); if (!fh) { dprintk(1,KERN_ERR"%s: zoran_open(): … ¥n", ZR_DEVNAME(zr)); return -ENOMEM; } New API : kzalloc (=kmalloc + memset) OLD NEW (from 2.6.14) 例

Slide 3

Slide 3 text

背景:大規模なコードのメンテは難しい Linuxでの実例:kmalloc()のメモリ確保とmemset()の ゼロクリアをkzalloc()に置換したい.どうする? fh = kmalloc(sizeof(struct zoran_fh), GFP_KERNEL); if (!fh) { dprintk(1,KERN_ERR"%s: zoran_open(): … ¥n", ZR_DEVNAME(zr)); return -ENOMEM; } memset(fh, 0, sizeof(struct zoran_fh)); fh = kzalloc(sizeof(struct zoran_fh), GFP_KERNEL); if (!fh) { dprintk(1,KERN_ERR"%s: zoran_open(): … ¥n", ZR_DEVNAME(zr)); return -ENOMEM; } New API : kzalloc (=kmalloc + memset) OLD NEW (from 2.6.14) 例 難しさ 1 そもそもコードが大規模で 人力は厳しい.

Slide 4

Slide 4 text

背景:大規模なコードのメンテは難しい Linuxでの実例:kmalloc()のメモリ確保とmemset()の ゼロクリアをkzalloc()に置換したい.どうする? fh = kmalloc(sizeof(struct zoran_fh), GFP_KERNEL); if (!fh) { dprintk(1,KERN_ERR"%s: zoran_open(): … ¥n", ZR_DEVNAME(zr)); return -ENOMEM; } memset(fh, 0, sizeof(struct zoran_fh)); fh = kzalloc(sizeof(struct zoran_fh), GFP_KERNEL); if (!fh) { dprintk(1,KERN_ERR"%s: zoran_open(): … ¥n", ZR_DEVNAME(zr)); return -ENOMEM; } New API : kzalloc (=kmalloc + memset) OLD NEW (from 2.6.14) 例 難しさ 1 そもそもコードが大規模で 人力は厳しい. 難しさ 2 全て修正したとして,それを パッチとして投函?

Slide 5

Slide 5 text

背景:大規模なコードのメンテは難しい Linuxでの実例:kmalloc()のメモリ確保とmemset()の ゼロクリアをkzalloc()に置換したい.どうする? fh = kmalloc(sizeof(struct zoran_fh), GFP_KERNEL); if (!fh) { dprintk(1,KERN_ERR"%s: zoran_open(): … ¥n", ZR_DEVNAME(zr)); return -ENOMEM; } memset(fh, 0, sizeof(struct zoran_fh)); fh = kzalloc(sizeof(struct zoran_fh), GFP_KERNEL); if (!fh) { dprintk(1,KERN_ERR"%s: zoran_open(): … ¥n", ZR_DEVNAME(zr)); return -ENOMEM; } New API : kzalloc (=kmalloc + memset) OLD NEW (from 2.6.14) 例 難しさ 1 そもそもコードが大規模で 人力は厳しい. 難しさ 2 全て修正したとして,それを パッチとして投函? 難しさ 3 (C言語の文法の範囲で) 色々なバリエーションが存在

Slide 6

Slide 6 text

背景:大規模なコードのメンテは難しい Linuxでの実例:kmalloc()のメモリ確保とmemset()の ゼロクリアをkzalloc()に置換したい.どうする? fh = kmalloc(sizeof(struct zoran_fh), GFP_KERNEL); if (!fh) { dprintk(1,KERN_ERR"%s: zoran_open(): … ¥n", ZR_DEVNAME(zr)); return -ENOMEM; } memset(fh, 0, sizeof(struct zoran_fh)); fh = kzalloc(sizeof(struct zoran_fh), GFP_KERNEL); if (!fh) { dprintk(1,KERN_ERR"%s: zoran_open(): … ¥n", ZR_DEVNAME(zr)); return -ENOMEM; } New API : kzalloc (=kmalloc + memset) OLD NEW (from 2.6.14) 例 難しさ 1 そもそもコードが大規模で 人力は厳しい. 難しさ 2 全て修正したとして,それを パッチとして投函? 難しさ 3 (C言語の文法の範囲で) 色々なバリエーションが存在 難しさ 4 カッコの対応などは 拡張正規表現で扱いにくい.

Slide 7

Slide 7 text

解決策: Semantic Patch Semantic Patch Language (SmPL) で変更を記述 ✓WYSIWYG スタイルで変換ルールを直感的に記述可能 ✓メタVariableやメタExpressionを使用した柔軟な表現 ✓C言語の文法を考慮したマッチング @@ expression E1,E2; @@ -x=kmalloc(E1,E2); +x=kzalloc(E1,E2); ... -memset(x,0,E1);

Slide 8

Slide 8 text

解決策: Semantic Patch Semantic Patch Language (SmPL) で変更を記述 ✓WYSIWYG スタイルで変換ルールを直感的に記述可能 ✓メタVariableやメタExpressionを使用した柔軟な表現 ✓C言語の文法を考慮したマッチング @@ expression E1,E2; @@ -x=kmalloc(E1,E2); +x=kzalloc(E1,E2); ... -memset(x,0,E1); 任意のExpressionにマッチング

Slide 9

Slide 9 text

解決策: Semantic Patch Semantic Patch Language (SmPL) で変更を記述 ✓WYSIWYG スタイルで変換ルールを直感的に記述可能 ✓メタVariableやメタExpressionを使用した柔軟な表現 ✓C言語の文法を考慮したマッチング @@ expression E1,E2; @@ -x=kmalloc(E1,E2); +x=kzalloc(E1,E2); ... -memset(x,0,E1); ここでの“...”はC言語の任意の statementsに最小マッチング

Slide 10

Slide 10 text

Coccinelle: SmPLの実行エンジン C言語プログラムのマッチングと変換ツール [EuroSys ‘08] @@ expressionx,E1,E2; @@ -x=kmalloc(E1,E2); +x=kzalloc(E1,E2); ... -memset(x,0,E1); Semantic Patch C言語のコード (e.g. Linux) 変換されたコード Coccinelle Logo: https://isc.tamu.edu/~lewing/linux/

Slide 11

Slide 11 text

Coccinelleの実装 原則: C言語のASTとSmPLのASTを比較・変換 C言語パーサ: /parsing_c/parser_c.mly SmPLパーサ: /parsing_cocci/parser_cocci_menhir.mly マッチングエンジン: /engine/cocci_vs_c.ml SmPL C言語ソースコード

Slide 12

Slide 12 text

Coccinelleの実装 原則: C言語のASTとSmPLのASTを比較・変換 C言語パーサ: /parsing_c/parser_c.mly SmPLパーサ: /parsing_cocci/parser_cocci_menhir.mly マッチングエンジン: /engine/cocci_vs_c.ml SmPL C言語ソースコード そんなに単純ではない (主に ... ,メタ変数の変換せいで)

Slide 13

Slide 13 text

Coccinelleを支えるModel Checking CTLの拡張 CTL-VW [POPL ‘09]によるSmPLの表現 C言語プログラムのControl Flow Graph Model Checker 一部[POPL’09]のスライドより引用

Slide 14

Slide 14 text

問題:CPPを用いた文法に沿わないコード 解決策?:CPPを処理した後でマッチング・変換 ダメな理由: 変換できても,変換先を元に戻せない. 解決策:直接コードをASTに変換し,マッチング いける理由:一般的に使用されるパターンは多くない. [CC ‘09]からの引用 Padioleauによると,ヒューリスティックにC言語文法拡張した コンパイラで,Linuxの98%以上コードをCPP無しで解析可能 [CC ‘09]

Slide 15

Slide 15 text

問題:gcc拡張を使ったコード 最近発表者が取り組んでいる試み • __attribute__がめんどくさい! • ほとんど意味なら同じASTを作りたくなるが, WYSIWYG原則とのトレードオフや微妙に意味が変わるケースも • マクロを用いてattributeを書くことも多い. __attribute__((attr1)) int __attribute__((attr2)) func() __attribute__((attr3)) { __attribute__((attr4(“args”))) char * var __attribute__((attr5)); return 42; } SEC(“section”) static user_type __iomem * func(char __unsed arg) __ release ;

Slide 16

Slide 16 text

少しは解釈してほしい Coccinelleの原則は構文レベルでマッチング・変換 問題:ASTは異なる以下のコードは,実用上は同一視したい. If ( x == NULL) { … } If (!x) { … } int i = 0x1; int i = (1); V.S. V.S. If (NULL == x) { … } V.S.

Slide 17

Slide 17 text

Isomorphism による利便性の向上 意味的には同じ(あるいは同じとして扱いたい)コード 片を Isomorphism としてグループ化することが可能 • 内部的には,元のSmPLからIsomorphicな複数のSmPL を生成し,それぞれをマッチング(全く解釈しない) • 典型的なIsomorphicなコードは standard.iso に定義済

Slide 18

Slide 18 text

Coccinelleの性能 [ATC ‘18] • 性能向上の工夫点 • Parmapによる並列実行 • SmPLから抽出したキーワードでgrep (内部実装) • 関数名や文字列等が特定のものだと,かなり性能に寄与 • まだつらい点 • 巨大プロジェクトは巨大(e.g. Linuxは16M LoC以上) • パースとバックトラックが遅い

Slide 19

Slide 19 text

Coccinelleの実際 CoccinelleのLinuxへの影響 ([ATC ’18]より)

Slide 20

Slide 20 text

Coccinelleの実際 CoccinelleのLinuxへの影響 ([ATC ’18]より) 2018年初めの時点で5000 コミット以上がCoccinelleを利用

Slide 21

Slide 21 text

Coccinelleの応用: バグの検出 • Linuxのバグ検出 [ASPLOS ‘11] • Linux 2.4.1 /2.6.x を対象に典型的なバグの存在を静的解析 • Linux 2.6.33 に対して 736 のバグを報告 • 従来の 「OSはドライバにバグが多い」という主張 [SOSP ‘01] は比較的新しいLinux 2.6.xにも当てはまるが,さらにHALレイ ヤの方がコードに対するバグが多いことを指摘 • OpenSSLのバグ検出への応用 [EDCC’10] • OpenSSLの特定の関数の返り値のチェックが不十分な問題 • そういった関数はソースコード全体に分散 • Coccinelleで機械的にチェックして 30 以上のバグを発見

Slide 22

Slide 22 text

その他 • 関連プロジェクト • Coccinelle4j [ECOOP ‘19] : Java向けのCoccinelle • Spoon:同じくJava向けでSmPLを扱える. • Semgrep:マッチングのみ,多言語対応 • Difftastic:マッチングのみ,トークン単位で比較 … • 開発体制 • 公式リポジトリ https://gitlab.inria.fr/coccinelle/coccinelle • 開発言語:OCaml • ライセンス:GPLv2 • メイン開発機関:INRIA • 使用プロジェクト:Linux, QEMU, systemd etc...

Slide 23

Slide 23 text

まとめ • Coccinelle/SmPLによる大規模ソースコードの管理 • Coccinelleの仕組み • CTL-VWによるモデルチェック • CPPも直接ASTに変換 • その他難しい点/性能 • Coccinelleの現在 • Linuxへのコミット 5000 ~ • バグ検出への応用

Slide 24

Slide 24 text

参考文献 1/2 [ATC ‘18] Lawall, Julia, and Gilles Muller. “Coccinelle: 10 years of automated evolution in the Linux kernel.” USENIX ATC 2018 [ASPLOS ‘11] Palix, Nicolas, et al. “Faults in Linux: Ten years later.” ASPLOS 2011. [POPL ‘09] Brunel, Julien, et al. “A Foundation for Flow-Based Program Matching Using Temporal Logic and Model Checking.” POPL 2009 [EuroSys ‘08] Padioleau, Yoann, et al. “Documenting and automating collateral evolutions in Linux device drivers.” EuroSys 2008

Slide 25

Slide 25 text

参考文献 2/2 [EDCC’ 10] Lawall, Julia, et al. "Finding error handling bugs in OpenSSL using Coccinelle.“ EDCC ’10 [SOSP ‘01] Chou, Andy, et al. "An empirical study of operating systems errors." SOSP 2001. [ECOOP ‘19] Kang, Hong Jin, et al. “Semantic patches for Java program transformation (experience report)” ECOOP 2019 [CC ‘09] Padioleau, Yoann. "Parsing C/C++ code without pre- processing." International Conference on Compiler Construction. Springer, Berlin, Heidelberg, 2009.