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

1893件以上のカーネルの不具合修正に貢献した再現用プログラムを自動生成するsyzkaller...

fujiihda
February 08, 2020

1893件以上のカーネルの不具合修正に貢献した再現用プログラムを自動生成するsyzkallerのテスト自動化技術 / syzkaller Kernel VM Kansai 10th

syzkaller は、Google の Dmitry Vyukov さんが開発し OSS として公開したカーネルのファジングツールで、2 年で 1500 件以上の Linux カーネルの不具合修正に貢献した実績を持ちます。自らが生成した複数の仮想マシンに対して問題の起きそうな入力を送り続けることで未発見の不具合を発見します。最小限の入力で不具合を再現させるための再試行を繰り返し、最終的には不具合を再現するための C 言語のプログラム生成を試みます。本資料は 2020/2/8 の「カーネル/VM探検隊@関西 10回目」の発表資料です。

fujiihda

February 08, 2020
Tweet

More Decks by fujiihda

Other Decks in Technology

Transcript

  1. 2 2 @fujiihda Whois fujiihda • 役割︓Infrastructure Engineer / 技術系コミュニティ運営等

    • 仕事︓技術調査、SRE、製品開発 • 経歴︓監視、OS (Linux)、コンテナ • 趣味︓セキュリティ技術
  2. 5 5 @fujiihda 今⽇話すこと カーネルのテスト⾃動化技術として Google の Dmitry Vyukov さんが開発し

    OSS として 公開した syzkaller (読み⽅︓シスコーラー) というファジングツールについて話します
  3. 6 @fujiihda 1. 背景 2. 実施内容 3. ドキュメント調査 4. ソースコード調査

    5. syzkaller の動作確認 6. まとめ 7. 個⼈的な振り返り 8. 今後 本⽇の流れ 時間の都合上 ここに注⼒
  4. 7 7 @fujiihda 背景 (ファジングと Linux のセキュリティ品質向上) • ファジング は未知の不具合や脆弱性の検出に適したテスト⼿法

    – 検査対象にランダムな⼊⼒データを送ることで意図的に例外を発⽣させる • セキュリティ品質 にファジングが与える影響が話題 – Open Source Summit Japan 2019 で複数講演者がファジングの貢献に⾔及 • 2017 年前後からファジングをはじめとする⾃動化された⾼度なテスト技術が普及 • リリース前にセキュリティ脆弱性が修正されるようになり CVE の件数は減少傾向 図︓ソフトウェア全体および Linux の CVE 件数の推移
  5. 8 8 @fujiihda 実施内容 • ドキュメント調査 (英語) – 公式ドキュメントの調査 –

    開発者による講演資料 / 講演映像の調査 – 学術論⽂の調査 – セキュリティカンファレンスの講演資料 / 講演映像の調査 – コミュニティの調査 (開発者の SNS 等を含む) • ソースコード調査 (ほぼ Go ⾔語) – Go ⾔語の⾔語仕様の差異の把握 – Go ⾔語の開発環境構築および⼿順の確⽴ – 全体構成把握および関数名とコメント⽂の斜め読み – 処理の順にソースコードの関数を追いかける • syzkaller の動作確認 (未発⾒の不具合の発⾒) – 環境構築および構築⼿順の確⽴ – ファジング実⾏と挙動の確認 – カーネルの I/O スケジューラに関連する use-after-free の 新規不具合の発⾒ – 再現⽤の C ⾔語のプログラム⽣成 全体像、各種構成要素、 ⼤まかな処理の流れ の解明 ソースコードにしかない 動作の仕組みや細かい 処理の流れを解明 動かして理解しつつ 未発⾒の不具合を探す
  6. 10 10 @fujiihda ドキュメント調査 - ⾼度なファジングの肝 • ⾼度なファジングの肝となる技術のひとつは検査対象で問題が起きそ うな⼊⼒データを⽣成する仕組み –

    この仕組みの⾼度化が発⾒困難な不具合の発⾒を可能にする – ファジングの⼊⼒データを⽣成する仕組みを分類した結果は次のとおりで、syzkaller はレベル 4 – ファジングのターゲットはネットワーク越し限定からそうでないものまで多岐に渡る 表︓ファジングの⼊⼒データを⽣成する仕組みの分類 レベル 仕組み 効率 網羅 備考 1 プロトコル等を考慮しない完全な ランダム × ◦ 実際の入力とは程遠い入力パターンが生成 される 2 キャプチャしたパケットをテンプ レートとしてその一部を改変 △ × キャプチャした範囲内の入力パターンしか生 成されない 3 プロトコル仕様や問題の起きそう な入力パターンを人間が実装し ておき活用 ◦ ◦ 主要な商用ツールで採用されている手法 4 レベル 3 に加えてソースコードの カバレッジを活用 ◎ ◦ syzkaller が採用している手法で、入力を変 異させるときの分岐条件にソースコードを活 用する
  7. 11 11 @fujiihda (補⾜) ここでの⼊⼒データとは具体的に何か • カーネルの⼊⼒インターフェイスはシステムコール • カーネルへの⼊⼒データは、例えばファイルをオープンする⽬的で使⽤される open()

    など、 300 種類ほどのシステムコールとその引数の組合せで構成される • つまり、カーネルのファジングでは、どのシステムコールを選択し、どのような引数で、 さらにはどのような組合せで送るかがポイントとなる 参照元︓https://www.kimullaa.com/entry/2020/01/05/191221
  8. 12 12 @fujiihda ドキュメント調査 - syzkaller とは • Google の

    Dmitry Vyukov さんによって開発された カーネルのファジングツール • 2 年間でカバレッジを 7 %カバーした時点で、 1500 件以上のカーネルの不具合修正に貢献 • 本⽇時点では、1893 件の不具合修正に貢献
  9. 13 13 @fujiihda ドキュメント調査 - syzkaller とは • 特徴 –

    ハイブリッドな仕組みにより効率良く システムコールのシーケンスを⽣成 • 試験対象プロトコル (システムコール) のテンプ レートが実装されている • ソースコードのコンパイル時にコンパイラが出⼒ するカバレッジを利⽤して⼊⼒を変える – 不具合発⾒の⼀連の流れがほぼすべて⾃動化 • ⾃らが⽣成した複数の試験対象の仮想マシンに 対して問題の起きそうな⼊⼒を送り続けて、 デバッグ⽀援機構を活⽤してその挙動を観測 • 発⾒した不具合を最⼩限の⼊⼒で再現させる ための再試⾏を⾃ら繰り返し、不具合を再現する ための C ⾔語のプログラム⽣成を試みる ユーザ空間 カーネル VMs (試験対象) syzkaller サーバ (仮想化ホスト) sshd syz- fuzzer syz- manager workdir/crashes/* workdir/corpus/* VMs 管理 rpc ssh, scp invoke syscalls カバレッジ syz- executor syz- executor syz- executor syz- executor カバレッジ テストケース ファジング対象のカーネル (KCOV 有効) UI 操作 # ./bin/syz-manager -config my.cfg 各構成要素の詳細は 次ページから解説 図︓syzkaller の構成図
  10. 14 14 @fujiihda (参考) Sanitizer とは • カーネルに存在する複数のデバッグ⽀援機構 – 動的テストツール

    – コンパイラの機能 (gcc および C ⾔語で利⽤可能) – メモリを破壊したことや違反したことなどをはっきり⽰すもの – 不具合を⽰してくれるのでファジングを補助してくれる – エラーが起きたら、エラーの深刻度に関わらず、 カーネルパニックを発⽣させる設定を使⽤する*1 (コンソールにパニックメッセージを出⼒) • syzkaller で使⽤している主な Sanitizer – KASAN (Kernel Address Sanitizer) • メモリアクセスエラーを検出 – KMSAN (Kernel Memory Sanitizer) • 初期化されていない読み取りを検出 – KTSAN (Kernel Thread Sanitizer) • 異なるスレッド間のデータの競合状態を検出 – UBSAN (Undefined Behavior Sanitizer) • 未定義の動作を引き起こす機能の使⽤を検出 図︓syzkaller の構成図 ユーザ空間 カーネル VMs (試験対象) syzkaller サーバ (仮想化ホスト) sshd syz- fuzzer syz- manager workdir/crashes/* workdir/corpus/* VMs 管理 rpc ssh, scp invoke syscalls カバレッジ syz- executor syz- executor syz- executor syz- executor カバレッジ テストケース ファジング対象のカーネル (KCOV 有効) UI 操作 # ./bin/syz-manager -config my.cfg
  11. 15 15 @fujiihda • コンパイラ (gcc) によって提供される機能 • カバレッジガイドファジングに適した形式でカーネル コードカバレッジ情報を⽣成

    • 使い⽅︓CONFIG_KCOV = y • 条件︓gcc 6.1.0 以降 (バージョン 231296 以降) ユーザ空間 カーネル VMs (試験対象) syzkaller サーバ (仮想化ホスト) sshd syz- fuzzer syz- manager workdir/crashes/* workdir/corpus/* VMs 管理 rpc ssh, scp invoke syscalls カバレッジ syz- executor syz- executor syz- executor syz- executor カバレッジ テストケース ファジング対象のカーネル (KCOV 有効) UI 操作 # ./bin/syz-manager -config my.cfg __fuzz_coverage(); if (...) { __fuzz_coverage(); ... } __fuzz_coverage(); if (...) { ... } 図︓KCOV 無効時と KCOV 有効時のイメージ 図︓syzkaller の構成図 (参考) KCOV (Kernel Code Coverage) とは
  12. 16 16 @fujiihda ドキュメント調査 - syzkaller の全体像 • syz-manager –

    仮想化ホスト上に存在するプロセス – 試験対象としての VMs の管理 (起動、監視、再起動) • この VMs はカーネルパニックのたびに再起動 – 試験対象としての VMs 内で syz-fuzzer プロセスを起動 – syz-fuzzer プロセスに指⽰を送る – workdir 上のコーパスとクラッシュ (次ページで説明) を更新 図︓syzkaller の構成図 ユーザ空間 カーネル VMs (試験対象) syzkaller サーバ (仮想化ホスト) sshd syz- fuzzer syz- manager workdir/crashes/* workdir/corpus/* VMs 管理 rpc ssh, scp invoke syscalls カバレッジ syz- executor syz- executor syz- executor syz- executor カバレッジ テストケース ファジング対象のカーネル (KCOV 有効) UI 操作 # ./bin/syz-manager -config my.cfg
  13. 17 17 @fujiihda ドキュメント調査 - syzkaller の全体像 • workdir/crashes/* –

    クラッシュに関わるアウトプット – アスタリスクにはハッシュ値が名前のフォルダが⽣成される – フォルダ内に次の情報を含む • description 事象を特定する件名 • logN (N=0〜99) syzkaller のログ • reportN (N=0〜99) カーネルクラッシュレポート • repro.cprog 再現⽤の C ⾔語のプログラム • repro.log • repro.prog • repro.report • repro.stats • reproM (M=0〜9) • workdir/corpus/* – コーパスは個別ファイルとして格納されているファズターゲット の⼊⼒セット – 理想的なコーパスは最⼤限のコードカバレッジを提供する 最⼩限の⼊⼒セット 図︓syzkaller の構成図 ユーザ空間 カーネル VMs (試験対象) syzkaller サーバ (仮想化ホスト) sshd syz- fuzzer syz- manager workdir/crashes/* workdir/corpus/* VMs 管理 rpc ssh, scp invoke syscalls カバレッジ syz- executor syz- executor syz- executor syz- executor カバレッジ テストケース ファジング対象のカーネル (KCOV 有効) UI 操作 # ./bin/syz-manager -config my.cfg
  14. 18 18 @fujiihda ドキュメント調査 - syzkaller の全体像 • syz-fuzzer –

    先述の不安定な試験対象 VMs 内に存在するプロセス – テストケース (⼊⼒) を⽣成、突然変異、最⼩化 – 任意の数の syz-executor プロセスを起動 – コーパスで⼊⼒を変える︖︖︖ → ドキュメント調査だけでは、なにをいっているのか いまいちわからなかった・・・ ソースコードを調査した結果︓ コーパスの有無で条件分岐して Generate 関数 もしくは Mutate 関数のいずれかを呼び出すことで システムコールのシーケンスを決める (詳細はソースコードを追いかけるときに説明) 図︓syzkaller の構成図 ユーザ空間 カーネル VMs (試験対象) syzkaller サーバ (仮想化ホスト) sshd syz- fuzzer syz- manager workdir/crashes/* workdir/corpus/* VMs 管理 rpc ssh, scp invoke syscalls カバレッジ syz- executor syz- executor syz- executor syz- executor カバレッジ テストケース ファジング対象のカーネル (KCOV 有効) UI 操作 # ./bin/syz-manager -config my.cfg
  15. 19 19 @fujiihda ドキュメント調査 - syzkaller の全体像 • syz-executor –

    先述の不安定な試験対象 VMs 内に任意の個数存在するプロセス – syz-fuzzer から syscalls のシーケンスを受け取る – syscalls のシーケンスを実⾏する – syscalls の実⾏結果を syz-fuzzer に送り返す – 単⼀の executor プロセスのことを Proc とも呼ぶ • Proc がカーネルに送るデータは ProgData とも呼ぶ 図︓syzkaller の構成図 ユーザ空間 カーネル VMs (試験対象) syzkaller サーバ (仮想化ホスト) sshd syz- fuzzer syz- manager workdir/crashes/* workdir/corpus/* VMs 管理 rpc ssh, scp invoke syscalls カバレッジ syz- executor syz- executor syz- executor syz- executor カバレッジ テストケース ファジング対象のカーネル (KCOV 有効) UI 操作 # ./bin/syz-manager -config my.cfg
  16. 21 21 @fujiihda ソースコード調査 - どの処理を調べるか • ⼀連の流れのなかで⾯⽩そうな処理 (executor) に

    関連するソースコードを処理順に追う 1. ssh で fuzzer が呼ばれたあとに executor を呼ぶまでの処理 (主に fuzzer) https://github.com/google/syzkaller/blob/master/syz-fuzzer/fuzzer.go 2. executor が呼ばれたあとに syscalls ⽣成までの処理 (主に executor) https://github.com/google/syzkaller/blob/master/syz-fuzzer/proc.go https://github.com/google/syzkaller/tree/master/pkg/ipc/ipc.go 図︓syzkaller の構成図 ユーザ空間 カーネル VMs (試験対象) syzkaller サーバ (仮想化ホスト) sshd syz- fuzzer syz- manager workdir/crashes/* workdir/corpus/* VMs 管理 rpc ssh, scp invoke syscalls カバレッジ syz- executor syz- executor syz- executor syz- executor カバレッジ テストケース ファジング対象のカーネル (KCOV 有効) UI 操作 # ./bin/syz-manager -config my.cfg 1 2
  17. 22 22 @fujiihda ソースコード調査 - fuzzer https://github.com/google/syzkaller/blob/master/syz-fuzzer/fuzzer.go func main() {

    (中略) for pid := 0; pid < *flagProcs; pid++ { proc, err := newProc(fuzzer, pid) if err != nil { log.Fatalf("failed to create proc: %v", err) } fuzzer.procs = append(fuzzer.procs, proc) go proc.loop() } fuzzer.pollLoop() } 引数として与えた FlagProcs の数だけ Proc (executor の プロセス) を⽣成 ゴルーチン (Go ⾔語で ⾔語仕様として提供され る並列処理の機能) で syz-fuzzer/proc.go の loop 関数を呼び出す 図︓syzkaller の構成図 ユーザ空間 カーネル VMs (試験対象) syzkaller サーバ (仮想化ホスト) sshd syz- fuzzer syz- manager workdir/crashes/* workdir/corpus/* VMs 管理 rpc ssh, scp invoke syscalls カバレッジ syz- executor syz- executor syz- executor syz- executor カバレッジ テストケース ファジング対象のカーネル (KCOV 有効) UI 操作 # ./bin/syz-manager -config my.cfg 1
  18. 23 23 @fujiihda ソースコード調査 - executor https://github.com/google/syzkaller/blob/master/syz-fuzzer/proc.go func (proc *Proc)

    loop() { generatePeriod := 100 if proc.fuzzer.config.Flags&ipc.FlagSignal == 0 { // If we don't have real coverage signal, generate programs more frequently // because fallback signal is weak. generatePeriod = 2 } for i := 0; ; i++ { item := proc.fuzzer.workQueue.dequeue() if item != nil { (中略) } ct := proc.fuzzer.choiceTable corpus := proc.fuzzer.corpusSnapshot() if len(corpus) == 0 || i%generatePeriod == 0 { // Generate a new prog. p := proc.fuzzer.target.Generate(proc.rnd, programLength, ct) log.Logf(1, "#%v: generated", proc.pid) proc.execute(proc.execOpts, p, ProgNormal, StatGenerate) } else { // Mutate an existing prog. p := corpus[proc.rnd.Intn(len(corpus))].Clone() p.Mutate(proc.rnd, programLength, ct, corpus) log.Logf(1, "#%v: mutated", proc.pid) proc.execute(proc.execOpts, p, ProgNormal, StatFuzz) } } } 図︓syzkaller の構成図 ユーザ空間 カーネル VMs (試験対象) syzkaller サーバ (仮想化ホスト) sshd syz- fuzzer syz- manager workdir/crashes/* workdir/corpus/* VMs 管理 rpc ssh, scp invoke syscalls カバレッジ syz- executor syz- executor syz- executor syz- executor カバレッジ テストケース ファジング対象のカーネル (KCOV 有効) UI 操作 # ./bin/syz-manager -config my.cfg 2 コーパス (ファズターゲッ トの⼊⼒セット) の有無で 条件分岐して、次のいず れかの関数を呼び出す Generate 関数 を呼び、引数 StatGenerate で execute 関 数を呼ぶ Mutate 関数を 呼び、引数 StatFuzz で execute 関数を 呼ぶ
  19. 24 24 @fujiihda ソースコード調査 - executor https://github.com/google/syzkaller/blob/master/syz-fuzzer/proc.go func (proc *Proc)

    execute(execOpts *ipc.ExecOpts, p *prog.Prog, flags ProgTypes, stat Stat) *ipc.ProgInfo { info := proc.executeRaw(execOpts, p, stat) calls, extra := proc.fuzzer.checkNewSignal(p, info) for _, callIndex := range calls { proc.enqueueCallTriage(p, flags, callIndex, info.Calls[callIndex]) } if extra { proc.enqueueCallTriage(p, flags, -1, info.Extra) } return info } 図︓syzkaller の構成図 ユーザ空間 カーネル VMs (試験対象) syzkaller サーバ (仮想化ホスト) sshd syz- fuzzer syz- manager workdir/crashes/* workdir/corpus/* VMs 管理 rpc ssh, scp invoke syscalls カバレッジ syz- executor syz- executor syz- executor syz- executor カバレッジ テストケース ファジング対象のカーネル (KCOV 有効) UI 操作 # ./bin/syz-manager -config my.cfg 2 executeRaw 関数を呼び 出す
  20. 25 25 @fujiihda ソースコード調査 - executor https://github.com/google/syzkaller/blob/master/syz-fuzzer/proc.go func (proc *Proc)

    executeRaw(opts *ipc.ExecOpts, p *prog.Prog, stat Stat) *ipc.ProgInfo { if opts.Flags&ipc.FlagDedupCover == 0 { log.Fatalf("dedup cover is not enabled") } // Limit concurrency window and do leak checking once in a while. ticket := proc.fuzzer.gate.Enter() defer proc.fuzzer.gate.Leave(ticket) proc.logProgram(opts, p) for try := 0; ; try++ { atomic.AddUint64(&proc.fuzzer.stats[stat], 1) output, info, hanged, err := proc.env.Exec(opts, p) if err != nil { if try > 10 { log.Fatalf("executor %v failed %v times:¥n%v", proc.pid, try, err) } log.Logf(4, "fuzzer detected executor failure='%v', retrying #%d", err, try+1) debug.FreeOSMemory() time.Sleep(time.Second) continue } log.Logf(2, "result hanged=%v: %s", hanged, output) return info } } 図︓syzkaller の構成図 ユーザ空間 カーネル VMs (試験対象) syzkaller サーバ (仮想化ホスト) sshd syz- fuzzer syz- manager workdir/crashes/* workdir/corpus/* VMs 管理 rpc ssh, scp invoke syscalls カバレッジ syz- executor syz- executor syz- executor syz- executor カバレッジ テストケース ファジング対象のカーネル (KCOV 有効) UI 操作 # ./bin/syz-manager -config my.cfg 2 env.Exec 関数 を呼び出す
  21. 26 26 @fujiihda ソースコード調査 - executor https://github.com/google/syzkaller/tree/master/pkg/ipc/ipc.go func (env *Env)

    Exec(opts *ExecOpts, p *prog.Prog) (output []byte, info *ProgInfo, hanged bool, err0 error) { // Copy-in serialized program. progSize, err := p.SerializeForExec(env.in) if err != nil { err0 = fmt.Errorf("failed to serialize: %v", err) return } var progData []byte if env.config.Flags&FlagUseShmem == 0 { progData = env.in[:progSize] } // Zero out the first two words (ncmd and nsig), so that we don't have garbage there // if executor crashes before writing non-garbage there. for i := 0; i < 4; i++ { env.out[i] = 0 } atomic.AddUint64(&env.StatExecs, 1) if env.cmd == nil { if p.Target.OS == "akaros" { // On akaros executor is actually ssh, // starting them too frequently leads to timeouts. <-rateLimit.C } tmpDirPath := "./" if p.Target.OS == "fuchsia" { tmpDirPath = "/data/" } atomic.AddUint64(&env.StatRestarts, 1) env.cmd, err0 = makeCommand(env.pid, env.bin, env.config, env.inFile, env.outFile, env.out, tmpDirPath) (以下省略) 図︓syzkaller の構成図 ユーザ空間 カーネル VMs (試験対象) syzkaller サーバ (仮想化ホスト) sshd syz- fuzzer syz- manager workdir/crashes/* workdir/corpus/* VMs 管理 rpc ssh, scp invoke syscalls カバレッジ syz- executor syz- executor syz- executor syz- executor カバレッジ テストケース ファジング対象のカーネル (KCOV 有効) UI 操作 # ./bin/syz-manager -config my.cfg 2 makeCommand 関数を呼び出す
  22. 27 27 @fujiihda ソースコード調査 - executor https://github.com/google/syzkaller/tree/master/pkg/ipc/ipc.go func makeCommand(pid int,

    bin []string, config *Config, inFile, outFile *os.File, outmem []byte, tmpDirPath string) (*command, error) { (中略) // executor->ipc command pipe. inrp, inwp, err := os.Pipe() if err != nil { return nil, fmt.Errorf("failed to create pipe: %v", err) } defer inwp.Close() c.inrp = inrp // ipc->executor command pipe. outrp, outwp, err := os.Pipe() if err != nil { return nil, fmt.Errorf("failed to create pipe: %v", err) } defer outrp.Close() c.outwp = outwp c.readDone = make(chan []byte, 1) c.exited = make(chan struct{}) cmd := osutil.Command(bin[0], bin[1:]...) if inFile != nil && outFile != nil { cmd.ExtraFiles = []*os.File{inFile, outFile} } cmd.Env = []string{} cmd.Dir = dir cmd.Stdin = outrp cmd.Stdout = inwp (以下省略) 図︓syzkaller の構成図 ユーザ空間 カーネル VMs (試験対象) syzkaller サーバ (仮想化ホスト) sshd syz- fuzzer syz- manager workdir/crashes/* workdir/corpus/* VMs 管理 rpc ssh, scp invoke syscalls カバレッジ syz- executor syz- executor syz- executor syz- executor カバレッジ テストケース ファジング対象のカーネル (KCOV 有効) UI 操作 # ./bin/syz-manager -config my.cfg 2 osutil.Command で executor 実⾏ executor の標準 ⼊⼒を os のパイ プにつなぐ ⼊出⼒リダイレクト
  23. 28 28 @fujiihda ソースコード調査 - executor https://github.com/google/syzkaller/tree/master/pkg/ipc/ipc.go func (c *command)

    exec(opts *ExecOpts, progData []byte) (output []byte, hanged bool, err0 error) { req := &executeReq{ magic: inMagic, envFlags: uint64(c.config.Flags), execFlags: uint64(opts.Flags), pid: uint64(c.pid), faultCall: uint64(opts.FaultCall), faultNth: uint64(opts.FaultNth), progSize: uint64(len(progData)), } reqData := (*[unsafe.Sizeof(*req)]byte)(unsafe.Pointer(req))[:] if _, err := c.outwp.Write(reqData); err != nil { output = <-c.readDone err0 = fmt.Errorf("executor %v: failed to write control pipe: %v", c.pid, err) return } if progData != nil { if _, err := c.outwp.Write(progData); err != nil { output = <-c.readDone err0 = fmt.Errorf("executor %v: failed to write control pipe: %v", c.pid, err) return } } 図︓syzkaller の構成図 ユーザ空間 カーネル VMs (試験対象) syzkaller サーバ (仮想化ホスト) sshd syz- fuzzer syz- manager workdir/crashes/* workdir/corpus/* VMs 管理 rpc ssh, scp invoke syscalls カバレッジ syz- executor syz- executor syz- executor syz- executor カバレッジ テストケース ファジング対象のカーネル (KCOV 有効) UI 操作 # ./bin/syz-manager -config my.cfg 2 progData を 継続して送る
  24. 30 30 @fujiihda syzkaller の動作確認 - ファジング実⾏時の画⾯の⼀部 # ./bin/syz-manager -config=my.cfg

    2019/06/05 03:53:20 loading corpus... 2019/06/05 03:53:20 serving http on http://127.0.0.1:56741 2019/06/05 03:53:20 serving rpc on tcp://[::]:37545 2019/06/05 03:53:20 booting test machines... 2019/06/05 03:53:20 wait for the connection from test machine... 2019/06/05 03:54:08 machine check: 2019/06/05 03:54:08 syscalls : 1380/2699 2019/06/05 03:54:08 code coverage : enabled 2019/06/05 03:54:08 comparison tracing : CONFIG_KCOV_ENABLE_COMPARISONS is not enabled 2019/06/05 03:54:08 extra coverage : extra coverage is not supported by the kernel 2019/06/05 03:54:08 setuid sandbox : enabled 2019/06/05 03:54:08 namespace sandbox : /proc/self/ns/user does not exist 2019/06/05 03:54:08 Android sandbox : /sys/fs/selinux/policy does not exist 2019/06/05 03:54:08 fault injection : CONFIG_FAULT_INJECTION is not enabled 2019/06/05 03:54:08 leak checking : CONFIG_DEBUG_KMEMLEAK is not enabled 2019/06/05 03:54:08 net packet injection : /dev/net/tun does not exist 2019/06/05 03:54:08 net device setup : enabled 2019/06/05 03:54:08 corpus : 3844 (0 deleted) 2019/06/05 03:54:10 VMs 4, executed 0, cover 0, crashes 0, repro 0 2019/06/05 03:54:20 VMs 4, executed 36, cover 3836, crashes 0, repro 0 2019/06/05 03:54:30 VMs 4, executed 776, cover 20662, crashes 0, repro 0 (中略) 2019/06/05 04:10:00 VMs 4, executed 70734, cover 62967, crashes 0, repro 0 2019/06/05 04:10:05 vm-3: crash: no output from test machine 2019/06/05 04:10:10 VMs 3, executed 70918, cover 62967, crashes 1, repro 0 (中略) 2019/06/05 04:14:02 VMs 4, executed 87377, cover 63959, crashes 1, repro 0 2019/06/05 04:14:05 vm-2: crash: no output from test machine 2019/06/05 04:14:12 VMs 3, executed 87614, cover 63960, crashes 2, repro 0 (中略) 2019/06/05 04:14:32 VMs 4, executed 87978, cover 63995, crashes 2, repro 0 2019/06/05 04:14:40 vm-3: crash: KASAN: use-after-free Read in blk_mq_free_rqs 2019/06/05 04:14:41 vm-1: running for 20m42.241115632s, restarting 2019/06/05 04:14:41 vm-0: running for 20m33.97704277s, restarting 2019/06/05 04:14:41 vm-2: running for 9.643775512s, restarting 2019/06/05 04:14:42 reproducing crash 'KASAN: use-after-free Read in blk_mq_free_rqs': 1158 programs, 4 VMs, timeouts [15s 1m0s 6m0s] 2019/06/05 04:14:42 VMs 0, executed 87978, cover 63995, crashes 3, repro 1 (繰り返す) 起動からわずか 20 分程で不具合 らしいものを発⾒ クラッシュ時に 事象を取り逃す こともある 同上 再現を試みる
  25. 31 31 @fujiihda syzkaller の動作確認 - 前ページの事象を再現するプログラム # cat bd744ffeabd8fcd8ae152d0c44dde8112311663f/repro.cprog

    // autogenerated by syzkaller (https://github.com/google/syzkaller) #define _GNU_SOURCE #include <endian.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/syscall.h> #include <sys/types.h> #include <unistd.h> uint64_t r[1] = {0xffffffffffffffff}; int main(void) { syscall(__NR_mmap, 0x20000000, 0x1000000, 3, 0x32, -1, 0); intptr_t res = 0; memcpy((void *)0x20000040, "/dev/loop-control¥000", 18); res = syscall(__NR_openat, 0xffffffffffffff9c, 0x20000040, 0x181000, 0); if (res != -1) r[0] = res; syscall(__NR_ioctl, r[0], 0x4c81, 0); return 0; } autogenerated by syzkaller
  26. 33 33 @fujiihda まとめ • ドキュメント調査 (英語) – 公式ドキュメントの調査 –

    開発者による講演資料 / 講演映像の調査 – 学術論⽂の調査 – セキュリティカンファレンスの講演資料 / 講演映像の調査 – コミュニティの調査 (開発者の SNS 等を含む) • ソースコード調査 (ほぼ Go ⾔語) – Go ⾔語の⾔語仕様の差異の把握 – Go ⾔語の開発環境構築および⼿順の確⽴ – 全体構成把握および関数名とコメント⽂の斜め読み – 処理の順にソースコードの関数を追いかける • syzkaller の動作確認 (未発⾒の不具合の発⾒) – 環境構築および構築⼿順の確⽴ – ファジング実⾏と挙動の確認 – カーネルの I/O スケジューラに関連する use-after-free の 新規不具合の発⾒ – 再現⽤の C ⾔語のプログラム⽣成 全体像、各種構成要素、 ⼤まかな処理の流れ の解明 ソースコードにしかない 動作の仕組みや細かい 処理の流れを解明 動かして理解しつつ 未発⾒の不具合を探す 完 完 完
  27. 34 34 @fujiihda 個⼈的な振り返り (⾃分向け教訓) • 知識ゼロから知らない⾔語の処理を追うときこそ急がば回れ – いきなりソースコード斜め読みは精神的に⾟い (熟練者向けなので個⼈的には⾮推奨)

    – たとえ遠回りに⾒えても、以下からはじめるとよい • 開発環境構築 • ⾔語仕様把握 • 各コンポーネントの役割や処理の流れを把握 • 実機による簡単な動作確認 • 不具合は常に最新版で探し、⾒つけたら放置せずに即直せ – カーネルの不具合は (素晴らしいことだが) ⾒つけたらすぐに直さないと誰かが修正する (c3e2219216c92919a6bd1711f340f5faa98695e6) – ⻑期間放置されている不具合は発⽣条件が不明確なものや修正が困難なものばかり (https://syzkaller.appspot.com/upstream)
  28. 35 35 @fujiihda 今後 • これから調べたい – テストケース (⼊⼒) を⽣成する仕組みのさらなる深掘り

    • テストケース (⼊⼒) の最⼩化 • テストケース (⼊⼒) の突然変異 – ファジング対象をシステムコール単位で絞り込む (できること確認済) • クラウドで提供されるマネージドなコンテナサービスは、 システムコールが限定されていることがあり、 その制限で有効なシステムコールのみに絞り込んで ファジングする – ファジング対象を機能単位で絞り込む (できること確認済) • ファイルシステムやネットワークなどの⼀部の機能 に限定してファジングする • ファジング関連の活動の続き – 4⽉末 新ネタ 投⼊ (予定)