Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Defense Against Undefined Behavior -未定義動作に対する防衛術-

Ea5ac91e16bd33383140d4c2753432d7?s=47 Cranberries
December 08, 2018
980

Defense Against Undefined Behavior -未定義動作に対する防衛術-

未定義動作に関する基礎的情報とその避け方の共有

Ea5ac91e16bd33383140d4c2753432d7?s=128

Cranberries

December 08, 2018
Tweet

Transcript

  1. Defense Against Undefined Behavior 未定義動作に対する防衛術 発表をご覧になった方々へのお詫び: Sequence Point・Sequence-beforeの解説内容に不備がありました。 間違いの訂正に関して赤字で修正・加筆を行っています。 クイズの解説スライドも追加しているのでご査収下さい。

    Sequence PointはC++03までのルールでした。 C++11からはSequence-beforeで説明されますです。 大変申し訳ありません。
  2. Rust 1.31.0 RELEASE!! Rust 2018 Edition Stable!! 使っていきましょう!! Non-lexical lifetimes!!

  3. 自己紹介  神:いなむのみたま@mitama_rs  CADDi株式会社に雇用されている  CADを解析にC++17を使っている  ラーメンが好き 

    けん玉が得意
  4. Undefined Behavior 101 未定義動作入門  Defined Behavior  規格書が明示的に一つの動作を定めている 

    Implementation Defined Behavior  複数の動作からコンパイラが動作を選べる  どの動作をするかドキュメントに明記されなければならない  Unspecified Behavior  複数の動作のどの動作をしても良いことになっている  Undefined Behavior(UB)  完全に何も決まっていない  何が起こってもおかしくない
  5. Why Study Undefined Behavior? なぜ未定義動作を学ぶのか?  UBはプログラム全体を未定義にする  UBが起こったときにはコンパイラは何をしてもいいことに起因する 

    例えば、UBを放置した結果6ヶ月後にレアケースでバグる  または、ツールチェインをかえたらエラーになって動かないなど  UBによるバグの修正には時間を要しがち  髪の毛も抜ける  髪の毛を守るためにUBを避ける技術を学ぼう!
  6. Defined Undefined Behavior 未定義動作だと定義されているもの  ヌルポインタのデリファレンス  配列のレンジ外アクセス  未初期化の変数の使用

     異なるポインタ型を介したオブジェクトへのアクセス(Strict Aliasing)  デストラクト済みの変数へのアクセス  副作用のない無限ループ  レースコンディション  整数型のサイズを超えたシフト演算  コンストラクタ・デストラクタからの純粋仮想関数の呼び出し  整数型のゼロ割り  整数型のオーバーフロー  他多数. . .
  7. Really easy explanation of UB よく分かる未定義動作の解説 C++ 料理 →

  8. Cooking and UB 料理と未定義動作  例えば塩と砂糖を間違えると…  料理全体が未定義に!  C++も同じ

     規格書が定めていることを書くことが重要  規格書が定めていないものはすべて未定義
  9. Recipe and Standard レシピと規格書  料理はレシピが規定し、C++プログラムは規格書が規定する 材料 完成品 レシピ コード

    挙動 規格書
  10. Quiz 突然ですがクイズです varA = 5; varA = ++varA + 2;

    ↑varA = ??? varB = 3; varB = varB++ + 2; ↑varB = ???
  11. Correct answer 正解はこちら varA = 5; varA = ++varA +

    2; // C++03, undefined behavior varA == 8; // C++11, defined varB = 3; varB = varB++ + 2; // C++03, undefined behavior varB == 5; // C++11, undefined behavior // C++17, defined
  12. Side Effects, Sequence and UB 副作用とシーケンスと未定義動作  同一のオブジェクトに対する2つの副作用があり、  それらの処理順が定められていない場合、

     未定義動作になる  (§1.9.15, C++11 / §4.6.17, C++17) x++ + ++x; // crazy expression
  13. Side Effects 副作用  式がある状態(変数など)を変更すると、副作用が生じる  雑に言うと、副作用は値を返す以外の何かである

  14. What happened with C++11 and C++17? C++11とC++17で何が変わった?  C++11:Squence point

    rules(C++03)の終わり, Sequence-before rules(C++11)の誕生  C++17: Sequence-before rulesの大幅な追加  ※C++11でSequence point rules自体の刷新が行われていました
  15. Sequence point rules (until C++11)  すべての式の副作用が完了し  次の式が評価されていないコード内の場所 

    通常は式の最後  full-expressionの途中はよくわからん状態  C++03のルール、C++11からはSequence-before rules
  16. Sequence-before and Sequence-after (from C++11) 副作用完了点の定義 なんらかの式AとBがあるとき  式Aは式Bの前にシーケンスすることができ、これは式Bが式Aの後にシーケンス されることと同じである

     不確定なシーケンスは、ある式が他の式の前にシーケンスされているところで 発生し、最初に起こるものは不明である  AがBより前にシーケンスされず、BがAより前にシーケンスされない場合、式A およびBの評価はシーケンスされない(重複し得る)
  17. In other words つまり  C++11以降では  ある式に式Aと式Bが含まれているとき  AとBの評価順が決まっているものと

     そうでないものがある  一意に評価順が定まらない場合  AとBに同一のオブジェクトへの副作用が含まれていたら未定義動作になる  Unsequencedと呼ばれる状態
  18. C++11:諸々の評価順序の規定  あらゆるオペレーターのオペランドの値の評価(ただし副作用を除く) はオペ レーターの結果の評価より先に評価される(ただし副作用を除く)  A + B という式ではAとBが評価されてから結果が評価される

     built-in assignmentがSequence-before rulesで順序規定された  A = B という式では式Bの評価(副作用を除く)が完了してから代入される  Built-in 前置++(--)の副作用はSequence-before rulesで順序規定された  ++AがA = A + 1に置き換えられるという事実による暗黙のルール  上記2つの順序規定より自明  ++Aの副作用は++Aの評価のあと完了する!  副作用の評価全然定まってねえ  式に副作用が2つあったとき、たいてい未定義の国へ直行
  19. C++17:諸々の副作用の評価順序規定  In a subscript expression E1[E2], every value computation

    and side-effect of E1 is sequenced before every value computation and side effect of E2  In a shift operator expression E1<<E2 and E1>>E2, every value computation and side-effect of E1 is sequenced before every value computation and side effect of E2  In every simple assignment expression E1=E2 and every compound assignment expression E1@=E2, every value computation and side-effect of E2 is sequenced before every value computation and side effect of E1  etc...  いい感じに副作用が完了するようになった!
  20. C++17:関数の引数評価順序  Until C++17: unspecified  C++17:引数の評価は、関数呼び出しの前に「順序付けされる」 f(g(h(var)));  関数に引数は呼び出し前に評価されるので

     fより前にg、gより前にhが評価される  fの評価時にはgの副作用が完了しており  gの評価時にはhの副作用が完了している
  21. ただし、引数の評価順序はunspecified int var; func( var = hoge(), huga(var) ); //

    ??? huga(var); var = hoge(); func(); var = hoge(); huga(var1); func(); どちらにもなりえる
  22. さきほどのクイズのポイント  同一変数の副作用の評価にSequence-before ruleが成立するか?

  23. さきほどのクイズ解説 varB = 3; varB = varB++ + 2; C++11

    1. ++varAの副作用はvarAの評価の後評価される 2. ++varA + 2 は結果より先にオペランドが評価される 3. varA = ++varA + 2 は ++varA + 2 が先に評価される 4. 代入で副作用が発生するが、 1. 2. 3. により代入前にインクリメントによる副作用 が終了する 5. varAの副作用はすべてsequenced 6. よってDefined Behavior Q.E.D.
  24. さきほどのクイズ解説 varA = 5; varA = ++varA + 2; C++11

    1. varA++の結果はその副作用よりも前に評価される 2. varA++ + 2 は結果より先にオペランドが評価される(副作用を除く) 3. varA = varA++ + 2 は varA++ + 2 が先に評価される(副作用を除く) 4. 代入で副作用が発生するが、varA++の副作用が代入前に完了確定していない 5. varAの副作用がunsequenced 6. よって、Undefined Behavior Q.E.D.
  25. さきほどのクイズ解説 varA = 5; varA = ++varA + 2; C++17

    1. varA++の結果はその副作用よりも前に評価される 2. varA++ + 2 は結果より先にオペランドが評価される(副作用を除く) 3. varA = varA++ + 2 は varA++ + 2 が先に評価され、 varA++ + 2 の副作用はすべて完了する 4. 代入で副作用が発生するが、 1. 2. 3. により代入前にインクリメントによる 副作用が終了する 5. varAの副作用はすべてsequenced 6. よって、Defined Behavior Q.E.D.
  26. Avoiding Undefined Behavior 未定義動作を避けるために

  27. General 基本だけど重要なこと  規格書を読んだり  cppreference.comやcpprefjpのようなサイトを読んだり  コードレビューをしたり  コーナーケースでテストしたり

     静的解析ツールを使ったり  コンパイラの警告耳を傾ける  -Wall –Wextra (-pedantic) とにかく、これらをサボらないこと
  28. Using Sanitizers サニタイザーの利用  clang  Address Sanitizer  Memory

    Sanitizer  Undefined Behavior Sanitizer  Thread Sanitizer  gcc  Address Sanitizer  Undefined Behavior Sanitizer
  29. Try Multiple Compilers 複数のコンパイラの利用  処理系によってUBが発生する可能性がある  複数のコンパイラでの確認は有効  新しいコンパイラほど賢い

     一度最新のコンパイラでコンパイルしてみる価値はある
  30. Undefined Behavior C++17 Compiler Warnings C++17対応コンパイラの未定義動作の警告  GCC 7.3, GCC

    8.2  operation on 'varB' may be undefined [-Wsequence-point]  clang 6.0, clang 7.0  unsequenced modification and access to 'varB' [-Wunsequenced]
  31. Using Online Compilers オンラインコンパイラの利用  WandboxやCompiler Explorerなど  複数のコンパイラで気軽にコンパイルできる! 

    いろいろなエラーが見られて楽しい!  最新の機能を試せる!
  32. Try –Wlifetime on Compiler Explorer Compiler Explorerで-Wlifetimeを試す

  33. Wandboxを使ってCIを回してみた  あー全部のコンパイラでテスト回すの面倒だなー  WandboxにAPIあるじゃん!?  テストコードをWandboxに送ってコンパイル&実行  Wandboxの間違った使い方かもしれん

  34. CBT  C++コードをinclude解析し  まとめてWandboxに送り  実行結果を表示できる  CLI 

    これを使ってテストを送りつけるぞ!
  35. Conclusion まとめ  UBが起こったあとのプログラムはすべてが未定義と思え  私はこの状態を未定義の国のアリスと呼んでいる  規格書に明記されてないものはすべて未定義  未定義動作をクリティカルなバグとして扱いましょう

  36. References 参考資料  https://www.copperspice.com/pdf/Undefined-Behavior-CppCon-2018.pdf  ISO/IEC 14882:2011 Programming Languages --

    C++  ISO/IEC 14882:2017 Programming Languages -- C++  http://d.hatena.ne.jp/yohhoy/20170104/p1(yohhoyの日記)  ついでにお世話になったツール・サイト  Compiler Explorer(https://godbolt.org/)  Radare2(https://github.com/radare/radare2)
  37. 補足 Q&A

  38. UBサニタイザしたら安心できる? そもそも何してるの?  UBサニタイザが通したからといって信用は禁物  UBサニタイザは大量の簡単なヒューリック

  39. 逆アセしてみる  サニタイザが何してるのか気になったので逆アセしてみた  まずサニタイザに引っかかります

  40. radare2でバイナリを解析する  https://github.com/radare/radare2 バイナリをr2でaa(analyze all)して ヒントアドレス付近を逆アセで確認。 ひたすらチェックが差し込まれてる。 このようなコードがいたるところに。 これがUBSanがやっていること。 年々賢くなっているとはいえ、

    チェック漏れもあるかも?