Я использую libfuzzer, и до сих пор это был отличный опыт. Мой код под fuzz полон таких ветвей:
bool fuzzingThisFunc() {
if (!checkSomething()) {
fmt::printf("error log");
return false;
}
...
return true;
}
Где fmt::printf
— функция из сторонней библиотеки (http://github.com/fmtlib/fmt).
Я чувствую, что после нескольких итераций фаззер входит в эту функцию и фактически начинает фаззить все ветки внутри нее (например, когда он использует DFS вместо BFS).
Я хотел бы добавить некоторый барьер или инструкцию к фаззеру, чтобы не вставлять инструменты в сторонние библиотеки, поэтому мой фаззер будет пытаться охватить только мой код.
Является ли это возможным?
Мне любопытно узнать, какой уровень оптимизации вы используете и насколько это помогает?
Libfuzzer поддерживает инструментирование на уровне исходного файла. Вариантом может быть создание сторонних библиотек без флага -fsanitize=fuzzer
.
Проверьте CFLAGS, переданные для настройки этих библиотек, чтобы снять этот флаг.
Библиотеки только для заголовков обычно включают шаблоны, как в случае с fmt. Они должны быть созданы во время компиляции. Я не вижу простого способа справиться с этим. Вы можете найти все используемые аргументы шаблона, создать преобразователь кода, который использует их с этими аргументами, исключить этот код из инструментовки и изменить свой вызывающий код, чтобы использовать эти экземпляры функций, но это очень сложно.
Когда код, который вы хотите не инструментировать, выполняет только ведение журнала или другие действия, которые можно пропустить без изменения поведения вашего приложения, вы можете сделать его условным для компиляции. Документы Libfuzzer предлагают использовать FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
define для пометки кода, который вы не хотите создавать для фаззинга. В первом случае это будет:
bool fuzzingThisFunc() {
if (!checkSomething()) {
#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
fmt::printf("error log");
#endif
return false;
}
...
return true;
}
или изменение кода библиотеки:
template <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_string<S>::value)>
inline int printf(const S& format_str, const Args&... args) {
#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
using context = basic_printf_context_t<char_t<S>>;
return vprintf(to_string_view(format_str),
make_format_args<context>(args...));
#else
return 0; //C printf returns number of characters written, I assume same for fmt.
#endif
}
Второй случай кодировать проще (одна модификация для каждой исключенной функции), но вам придется добавлять эту модификацию каждый раз, когда вы получаете новую версию fmt. В первом случае вам нужно изменить каждый исключенный сайт вызова функции.
В обоих случаях вы должны добавить -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
в CFLAGS конфигурации для фаззинга целевой сборки.
Интересная идея! Как насчет библиотек только для заголовков?
Изменил мой ответ. Libfuzzer может исследовать fmt::printf, если он может что-то изменить, что влияет на его внутреннее поведение. Очевидным выбором являются аргументы функции. Когда они жестко закодированы, как в случае с fmt::printf("журнал ошибок"), он не может их изменить. В этом случае ваши предположения о том, что libfuzzer застревает при изучении fmt, могут быть ошибочными.
Как только я отключил ведение журнала, мой фаззер сразу обнаружил проблему... Я использовал переключатель компиляции, чтобы отключить все регистраторы (к счастью, они были скрыты под макросом, поэтому я изменил 1 место).
Я был бы обеспокоен тем, что ошибки в вашем коде естественным образом проявляются в сторонних API с узким контрактом. В этом случае включение {fmt} в ваш поиск будет необходимо, хотя и дорого.