10. まとめ:最初の異常系を自分の言葉で説明する

10.1 一本の因果を再構成する

1. コンパイラはread_nameを含む命令列をELFへ入れる。
2. OSはELFを仮想メモリへ配置し、スレッドの実行を始める。
3. CPUはRIPが指す命令を取得し続ける。
4. call read_nameはmainの続きのアドレスをスタックへ保存する。
5. read_nameはその近くにname[16]を配置する。
6. readへはnameの先頭アドレスと128という最大値が渡る。
7. 長い入力はnameの範囲を超え、後続する戻り先を変える。
8. 書き込み時点では、まだRIPは変わらない。
9. 関数終了時のretが、壊れた戻り先をRIPへ入れる。
10. CPUは新しいRIPが指すwinから命令を取得する。

10.2 理解のセルフチェック

  1. 実行ファイルとプロセスの違いを説明できるか。
  2. 仮想アドレスと物理メモリが同じものではないと説明できるか。
  3. アドレス、ポインタ、配列長が別の情報だと説明できるか。
  4. RIP, RSP, RBPの役割を区別できるか。
  5. CPUが命令を取得してから次のRIPを決めるまでを説明できるか。
  6. 関数呼び出しに戻り先が必要な理由を説明できるか。
  7. callとretがRSPとRIPをどう変えるか説明できるか。
  8. 戻り先と局所配列が同じスタック上にあることを図示できるか。
  9. readのcountと実際の読み取り数を区別できるか。
  10. 長さの契約違反が境界外書き込みになる理由を説明できるか。
  11. 書き込みの瞬間と、壊れた戻り先が使われる瞬間を区別できるか。
  12. 単なるクラッシュとRIP制御の違いを説明できるか。
  13. winへ到達した後、CPUが命令を取得するまでを説明できるか。
  14. NXが防ぐことと、NXが防がないことを区別できるか。
  15. ASLRとPIEの関係を説明できるか。
  16. カナリやシャドウスタックが、因果のどの位置を切るか説明できるか。

この16問のうち、特に6、7、10、11、13を自分の言葉でつなげられれば、今回の目的は達成している。