Принципы работы syzkaller и его компонентов

Фаззингом называют динамический метод анализа кода, при котором на вход функциям/системным вызовам исследуемого программного обеспечения подаются случайные данные в попытке выявить дефекты.

Для фаззинг-тестирования ядра Linux на системном уровне предлагается использовать инструмент syzkaller. Данный фаззер поддерживает следующие ядра: Linux, Windows (с ограничениями), NetBSD, OpenBSD, FreeBSD, Android, Fuchsia, Akaros. В процессе своей работы syzkaller конструирует случайные программы на основе грамматик системных вызовов, запускает их на исследуемой платформе, получает обратную связь в виде достигнутого покрытия кода по ядру, мониторит лог ядра на предмет записей об ошибках. Случайные программы конструируются таким образом, чтобы увеличивать размер покрытия.

Для того, чтобы фаззинг-тестирование ядра могло эффективно выявлять ошибки, в ядре исследуемой ОС должны быть включены отладочные опции и опции самопроверки. В таком случае ядро сможет диагностировать дефект, когда поток управления дойдёт до некорректного кода, и выдать сообщение об ошибке в своем логе. Часто используемыми опциями диагностики являются проверки KASAN, KUBSAN, KMSAN, KCSAN.

KASAN - kernel address sanitizer (детектор ошибок доступа к памяти). Способен обнаруживать ошибки вида use-after-free (использование после освобождения ресурса) и out-of-bounds (доступ за границами объекта).

KUBSAN - kernel undefined behavior sanitizer используется для обнаружения ошибок неопределенного поведения.

KMSAN - kernel memory sanitizer используется для обнаружения ошибок доступа к неинициализированной памяти.

KCSAN - kernel concurrency sanitizer используется для обнаружения состояния гонок.

Существует также большое количество других опций диагностики. Так, в ядре Linux существуют опции для диагностики утечек памяти, некорректного использования блокировок, вызова планировщика в критических секциях, переполнения счетчиков ресурсов и множество иных.

Для измерения покрытия syzkaller использует механизм KCOV. Последний позволяет измерять покрытие по коду ядра с точностью до отдельного системного вызова. На основе данной обратной связи syzkaller строит корпус программ, позволяющий покрыть как можно большее количество блоков кода при анализе.

При обнаружении ошибки в ядре операционной системы, syzkaller пытается сгенерировать минимальную версию программы на языке Си, которая бы надежным образом вызывала срабатывание данной ошибки. В большом количестве случаев это удается сделать, в том числе для ошибок, срабатывающих в мультипоточной среде.

Все это при достижении хорошего уровня покрытия исследуемого кода позволяет выявлять большое количество дефектов в коде, в том числе, на ранних стадиях разработки драйверов. Для того, чтобы анализ драйвера был эффективен, необходимо описать его "точки входа" (аргументы ioctl, файлы /dev/, опции mount и т. д.) и структуры данных, которые пользовательская программа может использовать для взаимодействия с драйвером. Описание представляет собой грамматику системных вызовов и их потенциальных аргументов, на основе которой фаззер будет конструировать программы для воздействия на код драйвера.

В целом, проектом было выявлено более 1000 ошибок в коде ядра, некоторые из которых представляют собой эксплуатируемые уязвимости.

Инструмент включен в непрерывный цикл тестирования ядра Linux. Большое количество интерфейсов драйверов уже описано. В анализ включаются новые подсистемы ядра, например, стек USB ([1], [2]). Проект оказал существенное влияние на разработку ядра Linux.