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

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

Cranberries
December 08, 2018
1.4k

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

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

Cranberries

December 08, 2018
Tweet

Transcript

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

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

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

     異なるポインタ型を介したオブジェクトへのアクセス(Strict Aliasing)  デストラクト済みの変数へのアクセス  副作用のない無限ループ  レースコンディション  整数型のサイズを超えたシフト演算  コンストラクタ・デストラクタからの純粋仮想関数の呼び出し  整数型のゼロ割り  整数型のオーバーフロー  他多数. . .
  4. Cooking and UB 料理と未定義動作  例えば塩と砂糖を間違えると…  料理全体が未定義に!  C++も同じ

     規格書が定めていることを書くことが重要  規格書が定めていないものはすべて未定義
  5. Quiz 突然ですがクイズです varA = 5; varA = ++varA + 2;

    ↑varA = ??? varB = 3; varB = varB++ + 2; ↑varB = ???
  6. 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
  7. 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自体の刷新が行われていました
  8. Sequence point rules (until C++11)  すべての式の副作用が完了し  次の式が評価されていないコード内の場所 

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

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

     そうでないものがある  一意に評価順が定まらない場合  AとBに同一のオブジェクトへの副作用が含まれていたら未定義動作になる  Unsequencedと呼ばれる状態
  11. 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つあったとき、たいてい未定義の国へ直行
  12. 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...  いい感じに副作用が完了するようになった!
  13. C++17:関数の引数評価順序  Until C++17: unspecified  C++17:引数の評価は、関数呼び出しの前に「順序付けされる」 f(g(h(var)));  関数に引数は呼び出し前に評価されるので

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

    ??? huga(var); var = hoge(); func(); var = hoge(); huga(var1); func(); どちらにもなりえる
  15. さきほどのクイズ解説 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.
  16. さきほどのクイズ解説 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.
  17. さきほどのクイズ解説 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.
  18. General 基本だけど重要なこと  規格書を読んだり  cppreference.comやcpprefjpのようなサイトを読んだり  コードレビューをしたり  コーナーケースでテストしたり

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

    Sanitizer  Undefined Behavior Sanitizer  Thread Sanitizer  gcc  Address Sanitizer  Undefined Behavior Sanitizer
  20. 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]
  21. 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)