Применение AFL++ для фаззинга целей подготовленных для LibFuzzer в QEMU

Материал из ALT Linux Wiki

В AFL++ есть поддержка фаззинг-целей подготовленных для LibFuzzer.

Поэтому сборка целей LibFuzzer&AFL++ для qemu выглядит таким образом:

1) Подготовка исходников QEMU&Meson
Скачиваем meson, копилируем. Qemu скачиваем из репозитория ALT и подготавливаем.

RUN wget https://github.com/mesonbuild/meson/releases/download/0.61.3/meson-0.61.3.tar.gz && \
    tar -xf meson-0.61.3.tar.gz

RUN git clone http://git.altlinux.org/gears/q/qemu.git && \
    cd qemu && \
    git checkout 8.0.0-alt1.p10 && \
    gear  --commit  --rpmbuild  -- rpmbuild  --buildroot=/home/$cuidname/fuzz_target -bp

2) Задаём в переменных окружения компилятор AFL++
Иногда для проектов могут потребоваться задания линковщика как afl-clang-lto или afl-ld-lto, но мы соберем с LD=lld:

ENV CC=afl-clang-fast
ENV CXX=afl-clang-fast++
#ENV LD=afl-clang-lto
#ENV LD=afl-ld-lto
ENV LD=lld


3) Запускаем компиляцию QEMU с afl-clang
Компилируем исходники подготовленные gear на первом шаге:

RUN cd /home/$cuidname/RPM/BUILD/qemu-8.0.0 && \
        mkdir build_afl && cd build_afl && \
        ../configure --enable-fuzzing --disable-strip --disable-werror --meson=/home/user/meson-0.61.3/meson.py \
        && ninja -j8 qemu-fuzz-i386


4) Готовим исполняемые файлы для фаззинга на основе скомпилированного qemu-fuzz-i386
AFL++ в текущем виде(4.08с) умеет работать с одиночными целями LibFuzzer. А в qemu-fuzz-i386 собраны несколько целейподробнее, и AFL++ не может корректно распознать их по параметру -target, зато распознаёт цели по имени исполняемого файла. Поэтому нужно собрать возможные цели и создать копии qemu-fuzz-i386 с именем цели в названии файла:

targets=$(./qemu-fuzz-i386 | grep generic-fuzz | awk '$1 ~ /\*/  {print $2}') && \
        targets+=" " && targets+=$(./qemu-fuzz-i386 | grep -v generic | grep virtio- | awk '$1 ~ /\*/  {print $2}') && \
        targets+=" " && targets+=$(./qemu-fuzz-i386 | grep -v generic | grep i440fx- | awk '$1 ~ /\*/  {print $2}') && \
        for tgt in $targets ; do echo "Copy qemu-fuzz-i386 to /home/user/afl_fuzz_target with target name ${tgt}" ; \
                        cp qemu-fuzz-i386 /home/user/afl_fuzz_target/qemu-fuzz-i386-target-${tgt} ;\
                        cp -f qemu-fuzz-i386 qemu-fuzz-i386-target-${tgt} ; done



5) Запуск фаззинга AFL++&LibFuzzer
К сожалению, сейчас AFL++ не совсем корректно работает с фаззингом цели LibFuzzer в QEMU. При запуске переходит shmem и уходит в бесконечный цикл и таймаут. Похоже AFL ждёт, пока выполнится очередной инпут, а LibFuzzer уходит в режим фаззинга. Мы можем обойти эту ошибку отключив проверку исполняемого файла при запуске фаззига в AFL++:

AFL_SKIP_BIN_CHECK=1 AFL_MAP_SIZE=300000 AFL_SKIP_CPUFREQ=1 AFL_AUTORESUME=1 AFL_SKIP_CPUFREQ=1 afl-fuzz -t 500000 -i ${input_dir} -o ${OUT_DIR}/afl -S ${target_name} -- ${exec_target} @@"


Выбор версий компилятора и фаззера
К сожалению, не со всеми версиями clang&aflplusplus подобный подход работает. В моём случае получилось собрать с версиями clang-15(ALT Linux Team clang version 15.0.7), afl++ 4.08-9(commit hash: 61e27c6b54f7641a168b6acc6ecffb1754c10918), meson-0.61.3, qemu 8.0.0-alt1.p10.



Результаты применения AFL++ для фаззинга целей LibFuzzer в QEMU
Требовалось оценить, получится ли с использованием AFL++ значительно повысить прирост покрытия при фаззинге целей, которые уже до этого фаззились с помощью LibFuzzer в течении нескольких(3-4) месяцев. При этом новое покрыти уже практически не находилось фаззером для этих целей.

Эксперимент показал, что за месяц фаззинга прирост новых edges небольшой. В пределах 10-20. Тем не менее, он есть и этому частично способствует обмен корпусом между разными потоками фаззинга, которые фаззят разные цели LibFuzzer. Результаты прироста покрытия по целям. В зачёт идут только новые найденные потоком пути, не учитывая корпус полученным в рамках обмена между целями.

i440fx-qtest-reboot-fuzz/fuzzer_stats:corpus_found      : 10
i440fx-qos-noreset-fuzz/fuzzer_stats:corpus_found      : 226
generic-fuzz-ahci-hd/fuzzer_stats:corpus_found      : 11
generic-fuzz-virtio-rng/fuzzer_stats:corpus_found      : 13
generic-fuzz/fuzzer_stats:corpus_found      : 2
generic-fuzz-es1370/fuzzer_stats:corpus_found      : 11
generic-fuzz-virtio-blk/fuzzer_stats:corpus_found      : 9
master/fuzzer_stats:corpus_found      : 0
generic-fuzz-xhci/fuzzer_stats:corpus_found      : 11
generic-fuzz-cs4231a/fuzzer_stats:corpus_found      : 9
generic-fuzz-ne2k_pci/fuzzer_stats:corpus_found      : 7
generic-fuzz-floppy/fuzzer_stats:corpus_found      : 5
generic-fuzz-virtio-serial/fuzzer_stats:corpus_found      : 14
generic-fuzz-virtio-balloon/fuzzer_stats:corpus_found      : 11
generic-fuzz-sb16/fuzzer_stats:corpus_found      : 7
generic-fuzz-cirrus-vga/fuzzer_stats:corpus_found      : 8
generic-fuzz-am53c974/fuzzer_stats:corpus_found      : 11
generic-fuzz-ide-hd/fuzzer_stats:corpus_found      : 14
generic-fuzz-virtio-9p-synth/fuzzer_stats:corpus_found      : 13
generic-fuzz-vmxnet3/fuzzer_stats:corpus_found      : 7
generic-fuzz-ahci-atapi/fuzzer_stats:corpus_found      : 6
qemu-img_chain/fuzzer_stats:corpus_found      : 312
virtio-blk-flags-fuzz/fuzzer_stats:corpus_found      : 24
generic-fuzz-igb/fuzzer_stats:corpus_found      : 9
generic-fuzz-rtl8139/fuzzer_stats:corpus_found      : 7
generic-fuzz-virtio-scsi/fuzzer_stats:corpus_found      : 7
generic-fuzz-intel-hda/fuzzer_stats:corpus_found      : 16
generic-fuzz-virtio-9p/fuzzer_stats:corpus_found      : 10
generic-fuzz-megaraid/fuzzer_stats:corpus_found      : 16
generic-fuzz-pcnet/fuzzer_stats:corpus_found      : 9
generic-fuzz-ide-atapi/fuzzer_stats:corpus_found      : 7
generic-fuzz-pc-q35/fuzzer_stats:corpus_found      : 7
generic-fuzz-ehci/fuzzer_stats:corpus_found      : 15
virtio-net-socket-check-used/fuzzer_stats:corpus_found      : 13
generic-fuzz-i82550/fuzzer_stats:corpus_found      : 8
generic-fuzz-virtio-mouse/fuzzer_stats:corpus_found      : 15
generic-fuzz-virtio-gpu/fuzzer_stats:corpus_found      : 9
generic-fuzz-e1000/fuzzer_stats:corpus_found      : 12
generic-fuzz-sdhci-v3/fuzzer_stats:corpus_found      : 11
generic-fuzz-e1000e/fuzzer_stats:corpus_found      : 6
generic-fuzz-ac97/fuzzer_stats:corpus_found      : 11
generic-fuzz-parallel/fuzzer_stats:corpus_found      : 14
generic-fuzz-bochs-display/fuzzer_stats:corpus_found      : 12
generic-fuzz-ohci/fuzzer_stats:corpus_found      : 13
generic-fuzz-pc-i440fx/fuzzer_stats:corpus_found      : 8
virtio-scsi-flags-fuzz/fuzzer_stats:corpus_found      : 22
generic-fuzz-virtio-vga/fuzzer_stats:corpus_found      : 8


Выводы

Эксперимент показал, что нахождение нового покрытия с подключение AFL++ возможно. Однако в рамках текущей конфигурации прирост будет не слишком высоким.

Причины этого:

1) Достаточно глубокое покрытие целей фаззером LibFuzzer. Поскольку большей частью цели не слишком "сложные", они и так достаточно качественно фаззятся.

2) Медленная работа AFL++ в текущей конфигурации. Поскольку для фаззинга приходится выключать форк-сервер и обмен данными через оперативную память, то скорость фаззинга падает значительно.

3) Проблемы с оценкой прироста покрытия в LibFuzzer и AFL++. При использовании корпуса наработанного в LibFuzzer в качестве начального для AFL++, значительная часть корпуса отбрасывается как не добавляющая покрытия. Это странно, с учётом того, что LibFuzzer считал эти файлы значимыми и добавляющими покрытие. Возможно в текущей конфигурации часть исполняемого файла не инструментирована AFL++ или на оценку влияют еще какие-то неучтённые факторы.

Тем не менее, имеет смысл запускать фаззинг AFL++ для новых целей хотя бы для того, чтоб убедится в отсутствии прироста покрытия для них. Возможно имеет смысл использовать дополнительные средства инструментации, но этот вопрос требует дополнительного изучения.


[скорости фаззинга для разных фаззеров]