owned this note
owned this note
Published
Linked with GitHub
# AFL解析中…解析中
###### tags: `research` `fuzzing`
基本的にはLinux上での動作を記述する(コード読むとわかると思うが、大半はAPPLEとの差別化を行っている)
## 小技・tips
### 制御文字色々
制御文字というかエスケープシーケンスというか…。詳しくは[ここ](http://ascii-table.com/ansi-escape-sequences-vt-100.php)とか[ここ](http://www.termsys.demon.co.uk/vtansi.htm)を見るとよい。
ちなみに、以下のコマンドを入力するとclearコマンドっぽくなる。
```
echo -n "\x1b[H\1b[2J"
```
### strlen以外の文字数取得方法
strlen(str)を行わず、snprintf(NULL, 0, str)をすることで、書き込んだ文字数(strの文字列の数であって、指定文字数のほうではない)が戻り値となる。戻り値+1をmallocし、そこに対してsnprintf(番地, 戻り値+1, str)をすることで、改めて文字列をコピーできる。これにより、道のデータに対して文字数を取得し、そこに文字列を書き込むことが可能となる。流れは以下の通り。
```
len = snprintf(NULL, 0, str...)
ptr = malloc(sizeof(char) * (len + 1))
len = snprintf(ptr, len+1, str...)
```
### 割り算の切り上げ表記
`(a >> n) + !!(a & ((1 << n) - 1))` のような表記をすることで、 `a / (2^n)` の切り上げを行うことが可能となっている。
- `(a >> n)` : `a / (2^n)` の切り捨て
- `!!(a & ((1 << n) - 1))` : aの下位nビットが0であれば0、それ以外であれば1
## ヘッダファイル関連
### afl-as.h
### alloc-inl.h
#### マクロ・関数一覧
##### 共通事項
- `alloc_printf` : 小技・tipsの「strlen以外の文字数取得方法」を使用して、文字列をコピーする。その際、 `malloc` の代わりに `ck_alloc` を使用する。
- `ALLOC_CHECK_SIZE` : `MAX_ALLOC` よりメモリ取得数が多ければ `ABORT` を実行する。
- `ALLOC_CKECK_RESULT` : `malloc` が正常に実行されたかチェックする。
- `ALLOC_C1` : ポインタの先頭バイトを指し、 `ALLOC_MAGIC_C1` (dword=4バイト)を格納する。
- `ALLOC_S` : ポインタの先頭から5バイト目を指し、引数であるヒープ取得サイズ(dword=4バイト)を格納する。
- `ALLOC_C2` : ポインタの終端バイトを指し、`ALLOC_MAGIC_C2` (byte=1バイト)を格納する。
- `CHECK_PTR` : ポインタについて、以下のチェックを行う。
- `ALLOC_C1` と `ALLOC_MAGIC_C1` が一致しているか。
- 一致していない場合、 `ALLOC_C1` と `ALLOC_MAGIC_F` が一致しているか。
- 一致していればUse-after-freeと判定して `ABORT`
- 一致していなければcanaryが壊れたと判定して `ABORT`
- `ALLOC_C2` と `ALLOC_MAGIC_G2` が一致しているか。
- 一致していなければcanaryが壊れたと判定して `ABORT`
- `CHECK_PTR_EXPR` : `CHECK_PTR` を行う際、マクロの引数のタイプを明確にして行う。
- `DFL_ck_alloc_nozero` : 引数であるsize + 9バイトのヒープを確保し、ヘッダ( `ALLOC_C1 + size` )とフッタ( `ALLOC_MAGIC_C2` )を格納してヘッダの後のポインタを返す。
- `DFL_ck_alloc` : `DFL_ck_alloc_nozero` で取得した領域を全て0クリアして戻り値とする。
- `DFL_ck_free` : 引数のポインタを `CHECK_PTR` して、 ヘッダを `ALLOC_MAGIC_F` に変更後、 `free` を行う。この時、 `DEBUG_BUILD` が定義されていれば、フッタを0xFFにする。
- `DFL_ck_realloc` : `DFL_ck_alloc` の要領で `realloc` を行う。`DEBUG_BUILD` の指定で大きく変化するため、指定時と未指定時にそれぞれ記載。
- `DFL_ck_readlloc_block` : `DEBUG_BUILD` 指定時のみ、 `CHECK_PTR` を行った後、すでに要求している分のメモリを確保していればそのまま返す。そうでなければ、 `ALLOC_BLK_INC` 分だけ加算して `DFL_ck_realloc` を実行する。
- `DFL_ck_strdup` : `strlen` で引数の文字列の長さを求め、 `DFL_ck_alloc_nozero` のようにメモリを確保する。最後に確保したメモリに文字列をコピーして、そのポインタを返す。
- `DFL_ck_memdup` : `DFL_ck_strdup` のメモリ版。 `malloc` 後にヘッダとフッタをつけて、引数のポインタの場所をsize分コピーして、そのポインタを返す。
- `DFL_ck_memdup_str` : `DFL_ck_memdup` 後、戻り値のポインタの最後を0にしてるだけ。要はメモリコピー(文字列版)みたいな感じ。
- `ALLOC_BUCKETS` : `TRK_obj` の構造体数
- `TRK_obj` : メモリ番地、呼び出されたファイル関数、行数を格納する構造体。
- `TRKH` : 引数 `_ptr` を `(_ptr >> 16 XOR _ptr) % ALLOC_BUCKETS` した値を返す。
- `TRK_alloc_buf` : `TRKH(ptr)` 位置に `TRK_obj` を格納する関数。なお格納できる領域がなければ `realloc` を行うことでメモリを確保する。
- `TRK_free_buf` : `TRK[TRKH(ptr)]` にある `TRK_obj[ptr]` を0にする。
- `TRK_report` : 開放していないメモリを確保した際の関数、ファイル、行番号を全て表示する。
- `TRK_ck_alloc` : `DFL_ck_alloc` 後に `TRK_alloc_buf` を行う。
- `TRK_ck_realloc` : `DFL_ck_realloc` 後に `TRK_free_buf` および `TRK_alloc_buf` を行う。
- `TRK_ck_realloc_block` : `DFL_ck_realloc_block` 後に `TRK_free_buf` および `TRK_alloc_buf` を行う。
- `TRK_ck_strdup` : `DFL_ck_strdup` 後に `TRK_alloc_buf` を行う。
- `TRK_ck_memdup` : `DFL_ck_memdup` 後に `TRK_alloc_buf` を行う。
- `TRK_ck_memdup_str` : `DFL_ck_memdup_str` 後に `TRK_alloc_buf` を行う。
- `TRK_ck_free` : `TRK_free_buf` 後に `DFL_ck_free` を行う。
- `ck_alloc` : `TRK_ck_alloc` に直行する。
- `ck_alloc_nozero` : `TRK_ck_alloc` に直行する。
- `ck_realloc` : `TRK_ck_realloc` に直行する。
- `ck_realloc_block` : `TRK_ck_realloc_block` に直行する。
- `ck_strdup` : `TRK_ck_strdup` に直行する。
- `ck_memdup` : `TRK_ck_memdup` に直行する。
- `ck_memdup_str` : `TRK_ck_memdup_str` に直行する。
- `ck_free` : `TRK_ck_free` に直行する。
##### DEBUG_BUILD指定時
- `DFL_ck_realloc` : 以下のように動作する。
- 引数のポインタである `orig` の値により、以下の動作をする。
- `orig` が0x00でなければ、 `CHECK_PTR` を行い、 ヘッダを `ALLOC_MAGIC_F` とし、 `old_size` を `ALLOC_S` から取得し、 `orig` をヘッダ分減算する。
- `orig` が0x00であれば、何もしない。
- `realloc` を行い、またヘッダとフッタを格納する。最後に、以前のサイズより大きくなっていれば、大きくなった分のメモリを0クリアして返す。
- `ck_alloc` : `DFL_ck_alloc` と定義している。
- `ck_alloc_nozero` : `DFL_ck_alloc_nozero` と定義している。
- `ck_realloc` : `DFL_ck_realloc` と定義している。
- `ck_realloc_block` : `DFL_ck_realloc_block` と定義している。
- `ck_strdup` : `DFL_ck_strdup` と定義している。
- `ck_memdup` : `DFL_ck_memdup` と定義している。
- `ck_memdup_str` : `DFL_ck_memdup_str` と定義している。
- `ck_free` : `DFL_ck_free` と定義している。
- `alloc_report()` : なんか何も定義されていない。
- `TRK` : `TRK_obj` 構造体を格納するための配列。 `realloc` で可変2次元配列になる。
- `TRK_cnt` : `TRK` の要素ごとに格納されている `TRK_obj` 構造体の数を格納する。
- `alloc_report()` : `TRK_report()` に直行する。
##### DEBUG_BUILD未指定時
- `DFL_ck_realloc` : 以下のように動作する。
- 引数のポインタである `orig` の値により、以下の動作をする。
- `orig` が0x00でなければ、 `CHECK_PTR` を行い、 `old_size` を `ALLOC_S` から取得し、 `orig` をヘッダ分減算する。
- `orig` が0x00であれば、何もしない。
- `ret = malloc(size + ALLOC_OFF_TOTAL)` を行い、 `orig` の値により、以下の動作をする。
- `orig` が0x00でなければ、 `orig` を `ret` にコピーし、 `orig` を全て0xFFで埋めた後、 `free(orig)` を行う。
- `orig` が0x00であれば、何もしない。
- `ret` にヘッダとフッタを格納する。最後に、以前のサイズより大きくなっていれば、大きくなった分のメモリを0クリアして返す。
- `TRK` : `AFL_MAIN` 定義時のものと同じだが、外部宣言される。
- `TRK_cnt` : `AFL_MAIN` 定義時のものと同じだが、外部宣言される。
- `alloc_report()` : 何も行わない。
#### 定数一覧
| 定義名 | 意味 |
| -------- | -------- |
| `ALLOC_MAGIC_C1` | 0xFF00FF00(先頭のヘッダかつcanary) |
| `ALLOC_MAGIC_F` | 0xFE00FE00( `free` 後のヘッダ) |
| `ALLOC_MAGIC_C2` | 0xF0(フッタかつcanary) |
| `ALLOC_OFF_HEAD` | 8( `ALLOC_MAGIC_C1` + `size` で8バイト) |
| `ALLOC_OFF_TOTAL` | 9( `ALLOC_MAGIC_C1` + `ALLOC_MAGIC_C2` + `size` で9バイト) |
| `ALLOC_BLK_INC` | 256( `DFL_ck_realloc_block` 時に追加するメモリサイズ) |
| `ALLOC_BUCKETS` | 4096( `DEBUG_BUILD` 未指定時限定。) |
| `AFL_MAIN` | `afl_analyze.c` を一緒にコンパイルしていると定義されている |
### debug.h
- 色
- 様々な前景色と背景色がマクロ形式で定義されている。
- 色を使わない場合、定義は全て空文字となる。
- 表示用
- `SET_G1` : G1 special charsに変更する。(罫線が使えるようにする)
- `RESET_G1` : G1 character setに変更する。(罫線じゃない本来のものにする)
- `bSTART` : オプトアウトにより、マルチバイト文字を許す。(罫線を使えるようにする)
- `bSTOP` : オプトインにより、マルチバイト文字を許さなくする。(罫線じゃない本来のものにする)
- なお `FANCY_BOXES` が定義されていない場合、 `+` や `-` を使ってのボックス描画となる。
- 端末用
- `TERM_HOME` : 画面の位置を一番上にする。
- `TERM_CLEAR` : `TERM_HOME` 後に画面をクリアする。
- `cEOL` : カーソルの位置から右端までクリア
- `CURSOR_HIDE` : カーソルを非表示にする。
- `CURSOW_SHOW` : カーソルを表示する。
- デバッグ用
- `SAYF` : 実質 `printf` だが、オプションによっては `fprintf(stderr, ...)` となる。
- `WARNF` : 黄色の `[!]` と青色の `WARNING:` を表示し、警告を表示する。
- `ACTF` : 青色の `[*]` を表示し、現在行っていることを表示する。
- `BADF` : 赤色の `[-]` を表示し、fatal errorを表示する。
- `FATAL` : 重大なエラーにより、AFL自体が終了する際に、エラーが発生した場所を表示し、プログラムを終了する。
- `ABORT` : 何らかのエラーにより、AFL自体をabortする際に、停止した場所を表示し、プログラムをabortする。
- `PFATAL` : `FATAL` に加えて `perror()` の内容を表示する。
- `PRFATAL` : 引数である `res` の値によって `FATAL` か `PFATAL` を選択する。
- `ck_write` : `write` がうまくいかない場合、 `PRFATAL` を呼び出す。
- `ck_read` : `read` がうまくいかない場合、 `PRFATAL` を呼び出す。
### types.h
- u??=uint??_t(??には数字)
- x86_64かつu64の時のみ、u64=unsigned long long
- s??=int??_t(??には数字)
- MIN(_a, _b) = _aか_bの小さい方が出力
- MAX(_a, _b) = _aか_bの大きい方が出力
- SWAP16(_x) = 上位8ビットと下位8ビットを入れ替え
- SWAP32(_x) = 32ビットの値を8ビットごとに分け、上位ビットから8ビット毎にA, B, C, Dすると、ABCDからDCBAに変換する
- AFL_LLVM_PASSの有無によって変化
- あればAFL_R(x) = ランダム値をxで割ったあまりを返す
- なければR(x) = ランダム値をxで割ったあまりを返す
- 今の所代わりはないが、AFL_RかRかのマクロ名のみ変化がある感じ
- `STRINGIFY(x) = STRINGFY_INTERNAL(x) = #x` となり、文字列に変換される
- `MEM_BARRIER() = asm volatile("" ::: "memory")` となり、メモリバリアを行う。
- MEM_BARRIER()の前にあるload, store命令が、あとのload, store命令よりも前に実行されるようにする役割を持つ
- 拡張アセンブリ構文が使われている
- `asm(アセンブリ:出力オペランド:入力オペランド:破壊されるレジスタのリスト)
- volatileは最適化による、命令の削除を阻止する働きがある
- likely(_x)とunlikely(_x)は、それぞれ分岐予測に使われる。
- likely(_x): _xが真であれば高速に動作する
- unlikely(_x): _xが偽であれば高速に動作する
- 結局、 `_x` で発生する真偽のうち、どちらかの確率が非常に高いのであれば、likely()かunlikely()を使えば高速化できる
### config.h
様々な定数を定義している。
### hash.h
- x86_64の場合、64ビットのハッシュ計算が定義される。
- ROL64マクロは、64ビットの回転シフト
- hash32()は、64ビットのMurmurHashもどき(?)を行ったのち、下位32ビットを戻り値として返す
- MurmurHashみたいだけど、64ビット用に少し変更が加えられているっぽい
- x86_64以外の場合、32ビットのハッシュ計算が定義される。
- ROL32マクロは、32ビットの回転シフト
- hash32()は、32ビットのMurmurHashを行ったのち、32ビットを戻り値として返す
## プログラム関連
### afl-gcc.c
- `gcc -o test test.c` のように、通常の `gcc` コマンドに必要な入力を行うと動く
- AFLに必要となるコンパイルオプションを追加して、 `gcc` コマンドを実行する
- この時、 `-B` オプションでカレントディレクトリのリンカなどを優先的に実行するようにする
### afl-as.c
- `make` 時に、 `as` という名前の実行可能ファイルでコンパイルされる
- `afl-gcc.c` で `gcc` コマンドを実行する際、 `-B.` オプションが付いている
- `-B.` により、`as` という名前のリンカをカレントディレクトリから探すようになる
- つまり、ここではアセンブルを行う手前のファイルが作られている状態のファイル名がコマンドライン引数として与えられる
- トランポリンコードを使用して、作成する共有メモリ部分のエッジカバレッジをインクリメントするアセンブリを特定位置に追加する
- 基本的には `\t` + `jmp` 以外の条件分岐命令( `j` + `m以外` の命令)から始まる行の手前
- なお、関数ラベル後であれば常に計測可能であるが、条件分岐命令の場合、分岐先のラベルも正確に条件指定する必要がある(この辺りは400行付近を見るとある程度わかると思う)
- 用途としては、 `afl-gcc` でのコンパイル後にアセンブルを行うために用いられる
#### トランポリンコードとfork serverの準備
##### トランポリンコード
- afl-as.cで挿入される計測用コードは、以下のような流れになっている
- いくつかのレジスタを保存し `__afl_maybe_log` に遷移する。この時、 `rcx` に `id(乱数 % MAPSIZE)` を格納しておく。なお乱数は、アセンブリコード挿入時点で確定しているため、ファジング実行ごとに変化する訳ではない。
- AHにフラグを保存し、`__afl_global_area_ptr` の値を確認し、0であれば `__afl_setup` に遷移する。また0でなければ `__afl_store` に遷移する。
- `__afl_store` では `prev_id ^ id` を行い `rcx` に格納した後、 `prev_id` を `__afl_prev_loc` に格納し1ビットシフトする。最後に、 `__afl_prev_loc` の該当する位置をインクリメントし、そのまま `__afl_return` へ入る。
- `__afl_return` では、保存したフラグを復帰したのち、ret命令を行う
- `__afl_setup_failure` が0でなければ、 `__afl_return` に遷移する。
- `__afl_setup_failure` が0であれば、 `__afl_global_area_ptr` の値を取り出しテストを行ったのち、 `__afl_setup_first` に遷移する
- 様々なレジスタを保存したのち、 `r12` にスタックポインタを保存して `rsp` を0x10減算したのち、 `rsp` の下位4ビットを0にする。
- 減算して下位4ビットを0にするのは、スタックは16bit境界であることを前提としているため
- `.AFL_SHM_ENV` の環境変数を取得し、 結果が0であれば `__afl_setup_abort` へ遷移する
- `__afl_setup_abort` では、保存したレジスタを復帰したのち、 `__afl_return` へ遷移する
- 結果が0でなければ、結果を `atoi` で数値に変換する。
- その後、 `shmat` で共有メモリを作成する。この時、 `shmat` の結果が-1であれば `__afl_setup_abort` へ遷移する。
- `shmat` の結果である共有メモリアドレスを `__afl_area_ptr` および `__afl_global_area_ptr` に保存し、 `rdx` へ格納しておく。そしてそのまま `__afl_forkserver` へ入る。
- 2度 `rdx` をプッシュしたのち、 `FORKSRV_FD + 1` のファイルディスクリプタに対してデータを `write` する。戻り値が4でなければ `__afl_fork_resume` へ遷移する。
- `__afl_fork_resume` では、共有メモリのディスクリプタのクローズ、スタック領域からレジスタへの復帰を行い、 `__afl_store` へ遷移する。
- 戻り値が4であった場合、そのまま `__afl_fork_wait_loop` へ入る。
- `FORKSRV_FD` のファイルディスクリプタに対してファイルを `read` する。戻り値が4でなければ `__afl_die` へ遷移する。
- `__afl_die` では、 `exit(0)` に相当する命令が実行される。
- 4であれば、 `fork()` を行う。この時、戻り値が0未満であれば `__afl_die` へ遷移し、0であれば `__afl_fork_resume` へ遷移する。
- この時、 `FORKSRV_FD` および `FORKSRV_FD + 1` がクローズされるため、生成された子プロセスは通常のプログラム実行へと遷移していく。(main関数の最初に `__afl_maybe_log` が実行されるため)
- 次に `__afl_fork_pid` へ戻り値である子プロセスIDを格納し、子プロセスIDを `FORKSRV_FD + 1` のファイルディスクリプタに対して `write` する。
- その後、子プロセスIDに対して `waitpid` を実行し、戻り値が0以下であれば `__afl_die` へ遷移する。そうでなければ、`waitpid` で取得した `status` を子プロセスに対して `FORKSRV_FD + 1` を経由して `write` する。
- 最後に `__afl_fork_wait_loop` へ遷移することで、ループ処理となる。
### afl-fuzz.c
- 実際にファジングを行う部分
- `-i-` オプションと `-i<input directory name>` オプションの違いに注意
- `-i-` オプションでは、前回実行したファジングの続きが行うことができる
- `-o<output directory name>` オプションで前回実行したファジングでの出力ディレクトリを指定すれば、その中に前回実行したファジングの内容が記録されているため、続きを行うことが可能
- 他にも同期設定やタイムアウト時間の指定など、色々とオプションがある
- 今回の動的解析との組み合わせでは、 `-m` オプションが必要になる
- メモリ制限を行うオプション
- オプション認識以降、多くの前準備がある
- `dumb_mode` 時、 `crash_mode, qemu_mode` は使用できない
- 環境変数から、多くのデータを取得していく( `no_forkserver` や `no_cpu_meter_red` 、 `fast_cal` など)
- `save_cmdline` : コマンドライン引数を `orig_cmdline` に保存しておく
- `fix_up_banner` : バナー(AFLを動かしている時のタイトルみたいなもの)を作り出す
- `check_if_tty` : TTYにUIを作るか確認する。同時に、terminalのサイズも取り出しておく。
- `get_core_count` : 現在使用可能なCPUの数を取得する。また動作中のプロセス数も `/proc/stat` から抽出する。
- `bind_to_free_cpu` : CPUが2つ以上ある場合、使用されていないCPUに対してCPUアフィニティを設定する。
- `check_crash_handling` : `echo 'core' | sudo tee /proc/sys/kernel/core_pattern` を行っているか確認する。
- `check_cpu_governor` : CPUのクロック周波数の最小値と最大値を `/sys/devices/system/cpu/cpu0/cpufreq` から取得し、同値であるか確認する。
- `setup_post` : 環境変数 `AFL_POST_LIBRARY` の動的リンク名を取得し、 `dlopen` でロードする。その後、ロードしたライブラリから `afl_postprocess` 関数のアドレスを `dlsym` で取得し、ハンドルとして登録する。
- `setup_shm` : `virgin_bits, virgin_tmout, virgin_crash` を全て `0xff` に設定し、 共有メモリを用意する。共有メモリは `MAP_SIZE` 分存在しており、 `trace_bits` に用意される。
- `init_count_class16` : 実行カウント分類用の `count_class_lookup8` を、16ビット分に拡張した `count_class_lookup16` を作成する。(x86_64プロセッサの場合に使用する)
- `setup_dirs_fds` : 出力ディレクトリを色々と作成する。
- `read_testcases` : `<input directory>/queue` または `<input directory>` 内に存在するファイルの中身を、ファジングのシードとしてキューに挿入する
- `load_auto` : tokenを使用する場合、tokenを自動ロードする
- `pivot_inputs` : キューのデータを `<output directory>/queue` にファイルとして作成する
- `load_extras` : `-x` オプション時、 `extras_dir` が存在していればロードする。
- `find_timeout` : タイムアウトが設定されていない場合、タイムアウトを設定する(基本的には再実行時に、 `fuzzer_stats` を参照する)
- `detect_file_args` : argv中の `@@` を発見・処理する
- `setup_stdio_file` : out_fileが設定されていない場合、out_fdとして、 `<output directory>/.cur_input` を開いておく。
- `check_binary` : 解析対象プログラムが、ちゃんとしたものであるかをチェックする(存在するか、シェルではないか、マジックナンバー `ELF` が存在するか、などなど…)
- `get_qemu_argv` : `qemu_mode` である場合、コマンドライン引数を `qemu_mode` から持ってくる
- `perform_dry_run` : ユーザの用意した(これまでキューに格納した)テストケースを、実際に実行する
- `dumb_mode != 1` かつ forkserverが存在していなければforkserverを作成する
- この時、 `dup2` 関数により、標準入力を `.cur_input` に設定している。( `dup2(out_fd, 0)` みたいなやつ)
- 子プロセスでは、 解析対象プログラムの実行を行う。失敗した場合、 `trace_bits` に `EXEC_FAIL_SIG` を格納し、プロセスを終了する。
- 親プロセスでは、子プロセスからの終了ステータス( `exit()` )の結果を受け取り、4バイトであるかを確認する。
- `exec_cksum` があれば `first_trace` にコピーする。
- `stage_max` になるまで実行を繰り返す
- `.cur_input` に、 `use_mem` の内容を書き込む。
- 解析対象プログラムを実行する。
- 親→子: `prev_time_out` の内容を書き込む
- 子→親: `child_pid` を渡す
- trace_bitsを、 `count_class_look(8|16)` に変換する
- 実行結果や `trace_bits` など、様々な情報を元に更新処理を行う
- この時、最小限のパスでより多くのパスを追跡できるようにすることを目的としている
- trace_bitsからハッシュを生成・比較する。
- virgin_bitsとtrace_bitsを使い、新たなビットを発見できたか確認する
- `first_trace` と `trace_bits` が異なれば、変数があるとみなす
- 変数のパスを通るような値については、マークしておく
- 全て実行失敗したらERROR, `queue path` の5倍より多く失敗するとWARNが出る
- `cull_queue` : トレースできていないパスをトレースしている `top_rated` に対して `favored flag` を立てておく。なお冗長と思われていたパスに `favored flag` がついていれば、冗長でないと認識させる。
- `show_init_stats` : ファジング前の `Test case count, Bitmap range, Exec timing` を出力する。
- `find_start_position` : `-i-` オプション時、 `<output directory>/fuzzer_stats` から情報を取得し、 `cur_path` を取得する。
- `write_stats_file` : ここまでの状態を `<output directory>/fuzzer_stats` に書き込む。
- `save_auto` : `-x` オプション時、 `extras_dir` を保存しておく。
- よくわからないけど4秒停止する。
- 前準備終了後、本題のファジングに入っていく。
- `cull_queue` を行う。多分次ループ時用のやつ
- `queue_cur` がなければ(初期状態またはqueueのデータすべてをfuzzingし終えた場合)、以下の処理を行う。
- `queue_cycle` を初期化し、 `queue` を `queue_cur` に格納しておく。
- `find_start_position` で得た `current_queue` の分だけ `queue_cur->next` を `queue_cur` とする。
- UIを表示し、いくつかの処理を行った後、 `fuzz_one` 関数を実行する。
- 前回サイクルでの `queued_paths` と同じであれば、 `use_splicing` を1にするか、 `cycles_wo_finds` をインクリメントする。
-
#### `fuzz_one` 関数について
**ここから本番**
##### 前処理
1. 不必要なデータはファジングを行わないようにする。
- すでにファジングしたデータまたは興味深くないデータ(95%の確率でファジングしない)
- ダムモードではなく、興味深くないデータであり、変異戦略待ちの `queued_paths` が10以上の場合、
- 2サイクル(1サイクル= `queued_paths` を全て変異戦略し終えるまで)以降で既にファジングしたデータ(75%の確率でファジングしない)
- 上記以外のデータ(95%の確率でファジングしない)
2. CALIBRATION時点で失敗したデータはファジングしないようにする。
- `cal_failed` が3未満である場合、もう一度 `calibrate_case` を行う。
- `FAULT_ERROR` であればエラーを出力してプログラムを終了する。
- `stop_soon` か `res != crash_mode` であれば、現在のキューをスキップし、 `abandon_entry` へ遷移する。
3. トリミングしても問題ないデータはトリミングしておく。
- `dumb_mode` ではなく、現在のキューが`trim_mode` でなければ、 `trim_case` を実行し、入力のトリミングを行う。
- `abcdefghi` が入力であれば、 `abcdefghi→abcdi→abcd` と変化するっぽい
- この時、トリミングした入力で、現在のキューと同様の `trace_bits` が得られれば、入力をそちらに変更する。
- `trim_case` の戻り値が `FAULT_ERROR` であればエラーを出力し、 `stop_soon` であれば現在のキューをスキップし `abandon_entry` へ遷移する。
- どちらでもなければ、 `trim_mode` を1にし、現在のキューの文字列の長さが `fuzz_one` 関数の `len` と異なれば、今のうちに現在のキューの文字列の長さに変更しておく。
- 最後に `out_buf` もトリミングしたものにしておく。
`calculate_score` 関数によって点数付けしていく(点数は低い程よい)
- `calculate_score` 関数によって点数付けしていく。
1. 平均実行時間が低いほど点数がよくなる
2. カバレッジが高いほど点数がよくなる
3. `queue cycle` が大きいほど点数がよくなる
4. `depth` (実行した時の条件分岐の深さ…?または単にテストケースとしての深さ…?アセンブリ見た感じでは後者っぽいけど…)が大きいほど点数がよくなる
- 点数付け後、2つの条件に当てはまった場合には `havoc_stage` までスキップされる。
1. `-d` オプションがある、すでにファジングを行なっている、再開時に `output directory` にあった入力である
2. `-M` オプション関連の何か(使わない機能なので省略)
- どちらにも該当しなかった場合、 `doing_det` を1にし、スキップなしで戦略を実行する。
##### SIMPLE BITFLIP
一言で言えば、bitかbyte単位でのxor
まず、bit単位でのBITFLIPを説明する。
- まずは `stage_short` : `"flip1"` , `stage_max` : `len << 3` , `stage_name` : `bitflip 1/1` とする。
- その後 `stage_max` 回BITFLIPによるファジングを行うが、 `stage_cur >> 3` が該当するビット反転位置になっている。
- つまり入力全部を1bitずつフリップさせていく。
- ループ中 `common_fuzz_stuff` 関数内で `run_target` 関数を実行する。この時、 250回より多くタイムアウトする、または `Ctrl+C` などのシグナルがあれば、現在のキューをスキップする。
- スキップした場合、 `abandon_entry` に進む。
- スキップしなかった場合、 `save_if_interesting` 関数により、興味深い入力を保存する。
- `fault == crash_mode` であり、新たな遷移情報があれば、 `add_to_queue` 関数により、キューに追加する。
- 新たな遷移情報がなければ、興味深くないと認識する。
- `has_new_bits` 関数の戻り値が2(どの入力も見つけていない入力の場合の戻り値)であれば、 `queue_with_cov` をインクリメントする。
- 現在のキューのチェックサムを更新したのち、 `calibrate_case` 関数により、 `calibrate` を行う。正常な戻り値であれば、現在のキューの入力を更新し、 `keeping` を1にする。
- `fault` の値によって様々な処理を行う。
- `fault == FAULT_TMOUT` であれば、以下の処理を行う。
- タイムアウト回数をインクリメントし、 `unique_hangs` が500以上であれば `keeping` を返す。
- `dumb_mode` でなければ、 `simplify_trace` によって `trace_bits` を0x01と0x80に分けて、 `has_new_bits` 関数を実行する。( `has_new_bits` が0でなければ、 `keeping` を返す)
- `unique_tmouts` をインクリメントし、ユーザの設定したタイムアウト時間 `exec_tmout` よりも `hang_tmout` が大きければ、もう一度 `run_target` を実行し、 `!stop_soon` かつ `FAULT_CRASH` であれば `keep_as_crash` に遷移し、 `stop_soon` または `!FAULT_TMOUT` であれば `keeping` を返す。
- 最後に `<output directory>/hangs/` 以下にハング情報を記録し、 `unique_hangs` をインクリメントして終了する。
- `fault == FAULT_CRASH` であれば、以下の処理を行う。
- クラッシュ回数をインクリメントし、 `unique_crashes` が5000以上であれば `keeping` を返す。
- `dumb_mode` でなければ、 `simplify_trace` によって `trace_bits` を0x01と0x80に分けて、 `has_new_bits` 関数を実行する。( `has_new_bits` が0でなければ、 `keeping` を返す)
- `unique_crashes` が0であれば、 `write_crash_readme` 関数で `<output directory>/crashes/README.txt` にREADMEを書き込む。
- 最後に `<output directory>/crashes/` 以下にクラッシュ情報を記録し、 `unique_crashes` をインクリメントして終了する。
- `FAULT == FAULT_ERROR` であれば、エラーを出力してプログラムを終了する。
- その他であれば、 `keeping` を返す。
- `save_if_interesting` 後、 一定条件下で現状を出力し、0を返す。
- `stage_cur` の下位7ビットが7(8の倍数-1)であれば、以下の処理を実施する。
- `stage_cur == stage_max - 1` であり、BITFLIPした値が前回BITFLIPした値と同じ経路を辿るのであれば、 `out_buf` の最後の文字を `a_collect` に収集する。
- 収集文字列が3文字以上32文字以下であれば、 `maybe_add_auto` 関数により、自動辞書型( `a_extras` )の生成を行う
- 引数の `mem` (今回は `a_collect` )のうち、 `mem[0]` と同じ `mem[i]` ( `0<=i<=a_len-1` )がなければ `maybe_add_auto` 関数から抜ける。
- `len` が2または4の場合、 `mem` の中身が `interesting_xx` と同じものがあれば `maybe_add_auto` 関数から抜ける。 (多分興味深い値は自動辞書として使わない)
- 長さ順にソートされている `extras` のうち、すでに `mem` の値が存在していれば `maybe_add_auto` 関数から抜ける。
- `auto_changed` を1にし、 `a_extras` のうち、すでに `mem` の値が存在していれば、対象の `a_extras` の `hit_cnt` をインクリメントし、 `sort_a_extras` へ遷移する。
- `a_extras_cnt` が500未満であれば、ヒープ領域を用意して、そこに値と長さを格納する。
- そうでなければ250+random(250)から適当に値と長さを格納する。
- 最後に `sort_a_extras` で `qsort` を行い、 `count` の降順にソートする。なお最初の50個はさらに `size` の降順にソートする。
- BITFLIPした値が前回BITFLIPした値と同じ経路を辿らなければ、以下の処理を実行する。
- `a_len` が3文字以上32文字以下であれば、 `maybe_add_auto` 関数を実行し、 `a_len` を0にし、チェックサムを更新する。
- BITFLIPした値が現在のキューと同じ経路を辿らなければ、以下の処理を実行する。
- `out_buf` の最後の文字を `a_collect` に収集する。
- `new_hit_cnt` を 追加したキューの数である `queued_paths` と、新たなクラッシュを発生させた数である `unique_crashes` を加算した値にする。
- 最後に `stage_finds` に `new_hit_cnt - orig_hit_cnt` 、 `stage_cycly` に `stage_max` を加算する。
上記の処理のうち、2bitのBITFLIPおよび4bitのBITFLIPを行う。なお、どちらも自動辞書関連の処理は行わない。
次にbyte単位でのBITFLIPを説明する。こちらでは、以下の4種類の計算方法が存在する。
1. `EFF_APOS` : 引数の値を3ビット右シフトする。要するに `引数の値 / 8` の切り捨て。
2. `EFF_REM` : 引数の値の下位3ビットを取得する。
3. `EFF_ALEN` : `EFF_APOS` + `!!EFF_REM` を行う。( `!!value` は、0と1のどちらかになる)結果として、 `引数の値 / 8` の切り上げとなる。
4. `EFF_SPAN_ALEN(arg1, arg2)` : `EFF_APOS(arg1 + arg2 - 1) - EFF_APOS(arg1) + 1)` を行う。
以降の処理は以下の通りである。なおeff_mapでは、8byte単位での探索マップを作成している。
- eff_mapを `EFF_ALEN(len)` 分用意し、最初に1を格納しておく。そして `EFF_APOS(len - 1) != 0` になるまで、 `EFF_APOS(len - 1)` 番地の eff_mapを1にする。
- 以降、 `stage_max` 回( `len` )分だけ実行する。
- `out_buf` の `stage_cur` byte目を反転させ、 `common_fuzz_stuff` 関数を実行する。
- `EFF_APOS(stage_cur)` 番目のeff_mapが0であれば、以下の処理を実行する。
- `trace_bits` のチェックサムをとる。なお、 `len` が128byte未満であれば、 現在のキューのチェックサムを反転させたものを生成する。
- チェックサムと、現在のキューのチェックサムが異なれば、 `EFF_APOS(stage_cur)` 番目のeff_mapを1にする。
- 最後に反転させたbyteを元に戻す。
- eff_mapが1になった箇所が90%より多くて100%未満であれば、`memset(eff_map, 1, EFF_ALEN(len))` を行う。
- eff_mapを埋めた分だけ加算を行い、 `blocks_eff_*, new_hit_cnt, stage_finds, stage_cycles` の更新を行う。
上記処理のうち、「 `EFF_APOS(stage_cur)` 番目のeff_mapが〜」部分を省略したものが2byteと4byteのパターンである。
##### ARITHMETIC INC/DEC
1から35 ( `ARITH_MAX` )を順番に加算・減算する。
- `stage_max` に `2*len*ARITH_MAX` を格納し、以下の処理を実行する。
- `eff_map[EFF_APOS(i)]` ( `i=0〜len-1` )番目の値が0であれば、以下の処理を省略する。
- 1であった場合、以下の処理を実行する。
- `out_buf[i] ^ (out_buf[i] + j)` (j=0〜ARITH_MAX-1)を行い、その値を `could_be_bitflip` 関数に入れる。
- `引数値 & 1 == 0` の間、 `sh++` をしながら引数値を1ビット右シフトする。
- 引数値が1,3,15(1bit, 2bit, 4bit)のいずれかであれば1を返す
- 引数値が0xff, 0xffff, 0xffffffff(8bit, 16bit, 32bit) のいずれかであれば1を返す。
- 上記以外であれば0を返す。
- `could_be_bitflip` が0の場合(BITFLIP戦略では見つけられなかった値の場合)、 `out_buf[i] + j` した値で `common_fuzz_stuff` 関数を実行する。
- 上記処理の `out_buf[i] + j` を `out_buf[i] - j` とした状態でもう一度処理を実行する。
16bitおよび32bitの加算・減算では、エンディアンを考慮した設計になっている。以降、加算・減算で行われる処理部分を抜粋して行う。なお `i` および `j` は、上記8bitの場合と同じ意味を持つ。
- リトルエンディアン時にオーバフローを起こすと見なされるデータであり、かつBITFLIPで実行しなかった値の場合、 `common_fuzz_stuff` を行う。
- リトルエンディアン時にアンダーフローを起こすと見なされるデータであり、かつBITFLIPで実行しなかった値の場合、 `common_fuzz_stuff` を行う。
- ビッグエンディアン時にオーバフローを起こすと見なされるデータであり、かつBITFLIPで実行しなかった値の場合、 `common_fuzz_stuff` を行う。
- ビッグエンディアン時にアンダーフローを起こすと見なされるデータであり、かつBITFLIPで実行しなかった値の場合、 `common_fuzz_stuff` を行う。
また16bitから32bitへのbyte単位のBITFLIPへ遷移する際、 `len < 4` であれば32bitのBITFLIPをパスは省略される。
##### INTERESTING VALUES
固定値を挿入する戦略であり、8bit, 16bit, 32bitで分かれる。なお `stage_max` は `len * sizeof(interesting_*)` (*=8 or 16 or 32)である。
- `stage_max` に `len * sizeof(interesting_*)` (*=8 or 16 or 32)を格納し、以下の処理を実行する。
- `eff_map[EFF_APOS(i)]` ( `i=0〜len-1` )番目の値が0であれば、以下の処理を省略する。
- 1であった場合、以下の処理を実行する。
- `could_be_bitflip(out_buf[i] ^ (u8)interesting_*[j]) || could_be_arith(out_buf[i], (u8)interesting_*[j], 1)` (j=0〜sizeof(interesting_*)-1) であれば処理を省略し、そうでなければ固定値 `interesting_*[j]` を挿入して `common_fuzz_stuff` 関数を実行する。
- `could_be_arith` 関数では、まず引数1と引数2が同じであれば1を返す。
- 引数1と引数2を `8 * i` (i=0〜引数3)ビット右シフトし、異なっているか判定する。
- 上記判定が1回異なっていた場合、最後に異なっていた時の2つの値の差が、加算・減算ファジング時の35以下であれば、1を返す。
- 以降、16bitおよび32bitで同様の処理を繰り返す。
- 最後に、値を元に戻す。
16bit, 32bitも基本的には8bitと同様の処理を行うが、 `could_be_interest` 関数があることが大きく異なる。 `could_be_interest` 関数は以下の通り。結局のところ、これまで行ったINTERESTING VALUESの値があればパスするというもの。
- 引数1と引数2が同じであれば1を返す。
- 8bitでのINTERESTING VALUESと同じ値があれば1を返す。
- 16bitかつ引数4が0であれば0を返す。
- 16bitでのINTERESTING VALUESと同じ値があれば1を返す。
- `blen` が2より大きければ、リトルエンディアンも考慮する。
- 16bitかつ引数4が1であれば、32bitでのINTERESTING VLUAESと同じ値があれば1を返す。
- どれにも当てはまらなければ0を返す。
また16bitから32bitのINTERESTING VALUESに遷移する際、 `len < 4` であれば、32bitのINTERESTING VALUEは省略される。
##### DICTIONARY STUFF
この部分は辞書がなければ実行されない。辞書にはユーザ定義の辞書、自動生成した辞書の2種類が存在する。この2種類の辞書を用いて行われる処理は、以下の3つである。
1. ユーザ定義の辞書を使用して、文字列を上書きする。
2. ユーザ定義の辞書を使用して、文字列に連結・挿入を行う。
3. 自動生成した辞書を使用して、文字列を上書きする。
そのため `extras_cnt` が0であれば1.と2.は省略され、 `a_extras_cnt` が0であれば3.は省略される。それぞれの処理は以下の通り。
1. ユーザ定義の辞書を使用して、文字列を上書きする。
- `i` は `0〜len-1` 、`j` は `0〜extras_cnt-1` とする。
- 以下のいずれかの条件に合致した時、 `extras[j]` をスキップする。
1. `extras_cnt` が200より大きい、かつ `random(extras_cnt)` が200以上
2. 辞書の文字列の長さが、 `out_buf` より大きい
3. 辞書の文字列が `out_buf` と同じ
4. `i >> 3` から辞書文字列の長さを加味した間のeff_mapに1つでも1がある(byte単位のBITFLIPですでに変更した箇所であった場合、eff_mapは1になっている)
- 上記以外であれば、 `out_buf + i` 番目に辞書文字列を挿入し、 `commno_fuzz_stuff` 関数を実行する。
2. ユーザ定義の辞書を使用して、文字列に連結・挿入を行う。
- `i` は `0〜len-1` 、`j` は `0〜extras_cnt-1` とする。
- `out_buf` と辞書文字列を加算した値が1MBを超えていれば `extras[j]` をスキップする。
- 上記以外であれば、辞書文字列、 `out_buf` の順に連結し、 `common_fuzz_stuff` を実行する。
- `i` の値が進むにつれて、 `out_buf[i]` 番目の文字列を先に挿入するようになる。
- `i=3` であったとしたら、 `out_buf[0〜2], 辞書文字(extras[j]), out_buf[3〜len-1]` となる。
3. 自動生成した辞書を使用して、文字列を上書きする。
- `i` は `0〜len-1` 、`j` は `0〜MIN(a_extras_cnt-1, USE_AUTO_EXTRAS-1)` とする。なお `USE_AUTO_EXTRAS` は、使用する自動辞書の上限である。
- 以下のいずれかの条件に合致した時、 `a_extras[j]` をスキップする。
1. 辞書の文字列の長さが、 `out_buf` より大きい
2. 辞書の文字列が `out_buf` と同じ
3. `i >> 3` から自動辞書文字列の長さを加味した間のeff_mapに1つでも1がある(byte単位のBITFLIPですでに変更した箇所であった場合、eff_mapは1になっている)
- 上記以外であれば、 `out_buf + i` 番目に辞書文字列を挿入し、 `commno_fuzz_stuff` 関数を実行する。
ここまでの時点で現在のキューの `passed_det` が0であれば、 `<output directory>/queue/.state/deterministic/done` にファイルを用意し、 `passed_det` を1にしておく。(ここまでに来ないのは、 `common_fuzz_stuff` 関数内の `run_target` 後に一定条件で発生する。一定条件については、SIMPLE BITFLIP箇所を参照のこと。)
##### RANDOM HAVOC
- `splice_cycle` が0の時、以下の処理を行う
- () `doing_det` ( `performance_score` 関数実施後にたつフラグ)が立っていれば1024、そうでなければ256) * `perf_score` / `havoc_div` (平均実行速度の遅いものには大きな値がつく) / 100を `stage_max` とする。
- 点数の高い入力ほど、多くの突然変異を実施する。
- `splice_cycle` が1の時、以下の処理を行う
- 32 * `perf_score` / havoc_div / 100を `stage_max` とする。
- なお、どちらに置いても `stage_max < 16` であれば `stage_max = 16` とする。
- 以下、 `stage_max` 回のループを行う。
- `2〜2^n` (n=0〜7のランダム値)を `use_stacking` に代入し、以降 `use_stacking` 回のループを行う。
- `i` は、 `0〜use_stacking-1` とする。
- 以下、0〜16(正確には15+(0or2)のランダム値)によって処理が変化する。なお、0or2の部分については、登録辞書があれば0、自動辞書があれば2となる。
0. 1byte単位でのBITFLIP
1. 1byte単位でのINTERESTING VALUES(位置はランダム)
2. 2byte単位でのINTERESTING VALUES(位置はランダム)。なお0〜1のランダム値が0であればビッグエンディアン、1であればリトルエンディアンの処理となる。
3. 4byte単位でのINTERESTING VALUES(位置はランダム)。なお0〜1のランダム値が0であればビッグエンディアン、1であればリトルエンディアンの処理となる。
4. 1byte単位でのARITHMETHIC DEC(位置・減算値はランダム)。
5. 1byte単位でのARITHMETHIC INC(位置・加算値はランダム)。
6. 2byte単位でのARITHMETHIC DEC(位置・減算値はランダム)。なお0〜1のランダム値が0であればビッグエンディアン、1であればリトルエンディアンの処理となる。
7. 2byte単位でのARITHMETHIC INC(位置・加算値はランダム)。なお0〜1のランダム値が0であればビッグエンディアン、1であればリトルエンディアンの処理となる。
8. 4byte単位でのARITHMETHIC DEC(位置・減算値はランダム)。なお0〜1のランダム値が0であればビッグエンディアン、1であればリトルエンディアンの処理となる。
9. 4byte単位でのARITHMETHIC INC(位置・加算値はランダム)。なお0〜1のランダム値が0であればビッグエンディアン、1であればリトルエンディアンの処理となる。
10. 1〜255のランダム値を使用したBITFLIP(位置はランダム)。
11. `out_buf` のデータを一部削除する(位置・削除長はランダム)。
- `choose_block_len` 関数で、削除する長さを指定する。
- switch文で使用する値は0〜MIN(queue_cycle, 3)-1のランダム値で決められる。なお実行時間が10分を超えていなければ強制的に0とする。
0. `min_value=1, max_value=32` とする。
1. `min_value=32, max_value=128` とする。
2. (その他) 10分の1の確率で `min_value=1500, max_value=32768` とし、それ以外の場合は `min_value=128, max_value=1500` とする。
- 上記で決めた値のうち、 `min_value` が引数1(変異対象文字列の長さ)以上であれば、 `min_value=1` とする。
- `min_value + UR(MIN(max_value, limit) - min_value + 1)` を返す。
12. 11と同様の処理を行う。
13. `out_buf` に対して、自身の一部を挿入する(位置・長さはランダム)。
- 75%の挿入では、自身の内部に自身をランダムバイト分挿入する。
- 25%の挿入では、自身の1byteをランダムバイト分挿入する。なお50%の確率で、0〜255の値が代わりに挿入される。
14. `out_buf` に対して、自身の一部を上書きする(位置・長さはランダム)。
- 75%の上書きでは、自身の内部に自身をランダムバイト分挿入する。
- 25%の上書きでは、自身の1byteをランダムバイト分上書きする。なお50%の買う率で、0〜255の値が代わりに上書きされる。
15. 辞書を使用して上書きする(位置・使用辞書はランダム)。
- 登録辞書がないか、または自動辞書がある状態かつ50%の確率で自動辞書を使用する。
- 上記の条件でなければ、登録辞書を使用する。
16. 辞書を使用して挿入する(位置・使用辞書はランダム)。
- 登録辞書がないか、または自動辞書がある状態かつ50%の確率で自動辞書を使用する。
- 上記の条件でなければ、登録辞書を使用する。
- 上記の変異処理を `i` 回繰り返す。
- `common_fuzz_stuff` を実行し、 `out_buf` を元の値に戻す。
- `queued_paths` が `common_fuzz_stuff` 前後で比較して一致しなかった時、以下の処理を実行する。
- `perf_score` が1600以下であれば、 `stage_max` および `perf_score` を2倍にする。
##### SPLICING
データをsplicingする処理。
- `use_splicing` が1( `-M` オプション未指定時)、かつ `splice_cycle++` が15未満、かつ `queued_paths` が2以上、かつ現在のキューの文字列長が2以上であれば以下の処理を実施する。
- `in_buf` と `orig_in` が一致しない場合、 `in_buf` を元に戻す。
- 現在キューに格納されているものをランダムに取得する。
- ランダムに取得したキューのID( `fuzz_one` をするたびに増える)が100を超えていた場合、 `tid` が0になるまでキューの連結リストをたどる。(辿ったキューを `target` とする)
- `target` が2バイト未満または現在のキューでなくなるまで、キューの連結リストをたどる。(この時、たどるたびに `splicing_with` をインクリメントする)
- `target` が空になっていた場合、最初に戻り、もう一度SPLICINGを行う。
- `target` のファイルを開き、`target` の値と `in_buf` において、最初と最後に異なっていたbyteの位置を取得する。(それぞれ `f_diff` と `l_diff` とする)
- SPLICINGできそうになければ、最初に戻り、もう一度SPLICINGを行う。
- できそうであれば、 `f_diff+ UR(l_diff - f_diff)` を計算する( `split_at` とする)
- 最後に、 `in_buf[0〜split_at]` をコピーして `havoc_stage` (RANDOM HAVOC戦略)へ遷移する。
- 条件に合わなかった場合、 `ret_val` を0にして、 `abandon_entry` へ遷移する。
##### `abandon_entry`
ここでは、 `splicing_with` を-1にし、 `!stop_soon && !queue_cur->cal_failed && !queue_cur->was_fuzzed` であれば、現在のキューをすでにファジングしたとみなす( `pending_not_fuzzed` をデクリメントにし、 現在のキューに `favored` がマークされていれば `pending_favored` をデクリメントする)。
次に、 `mmap` していたファジング用の入力ファイルを `munmap` する。最後に、 `out_buf` および `eff_map` のメモリ領域を解除し、 `ret_val` を返す。