Svace API

Мы планируем добавить в анализатор API для того, чтобы пользователи могли создавать свои собственные детекторы.

Мотивация для создания Svace API

На вход анализатору Svace подаётся скрипт сборки проекта. Анализатор перехватывает вызовы компиляторов и линковщиков и строит внутреннее представление для кода анализируемого проекта. Это внутреннее представление содержит множество информации об исходном проекте: информацию о взаимосвязи модулей, код используемых функций на низкоуровневом языке.

Анализатор на основе промежуточного представления осуществляет поиск ошибок в оригинальном коде. Svace находит множество ошибок самых разных типов, среди которых:

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

Написание детекторов для таких библиотек разработчиками Svace может быть невозможно по разным причинам:

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

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

Сложности кастомизации

Для кастомизации анализатора требуется способ, который будет простым, лаконичным и в то же время достаточно функциональным. Мы посчитали, что лучшим способом будет реализация новой функциональности в виде кода на языке Java. Так как наши пользователи – программисты, то для них расширение функциональности в виде кода будет привычным и наиболее удобным способом.

Как и с любым другим открытым API возникает сложность обратной совместимости. По мере разработки API будет расширяться новыми возможностями и изменяться. Это может приводить к тому, что написанные пользовательские детекторы либо не будут вообще компилироваться с новой версией Svace, либо будут неправильно работать.

Кроме этого создание детекторов для статического анализа требует опыта в этой сфере, без которого даже опытные программисты будут допускать довольно простые, с нашей точки зрения, ошибки.

Поэтому при разработке API для создания детекторов, мы исходили из следующих предпосылок:

В ближайшем релизе Svace будет предоставлена базовая функциональность для написания простых детекторов, которые чуть лучше, чем утилита grep. Мы будем расширять эту функциональность на основе обратной связи от пользователей.

В настоящий момент реализовано два вида API:

Возможно, по мере интереса, мы предоставим большее количество возможностей для кастомизации.

Общая схема

Расширение анализатора было выполнено на основе модулей языка Java (появились в версии 9). Система модулей позволяет анализатору загружать код, который был скомпилирован после выпуска Svace. При этом пользовательские плагины проверяются компилятором Java на наличие ошибок.

Дистрибутив Svace будет содержать файл svace-api.jar, предоставляющий необходимые интерфейсы, и не имеющий никакие зависимости.

Все плагины должны быть помещены в директорию svace/plugins в виде jar-файлов. Каждый плагин должен иметь зависимость от svace-api.jar. Плагин должен реализовывать либо интерфейс SvaceLightPlugin, либо интерфейс SvaceIrPlugin в зависимости от используемого API. При запуске анализатор подгрузит код добавленных детекторов и выполнит его.

Оба интерфейса SvaceLightPlugin и SvaceIrPlugin имеют общую часть, которая вынесена в интерфейс SvaceAPIPlugin. Наиболее важным методом этого интерфейса является метод registerWarningType, позволяющий зарегистрировать новый тип предупреждения.

Пример реализации:

@Override
public void registerWarningType(SvaceUserWarningRegister register) {
	WarningTypeProperties props = WarningTypeProperties.create(Language.CXX)
                .setSeverity(Severity.Major)
                .setCweSet(266)
                .setDescription("Checker for incorrect use privileges by linux functions.");

	badPrivilegeWarning = register.registerWarning("BAD_PRIVILEGE", props);

Код выше позволяет зарегистрировать новый тип предупреждения; указать язык, для которого он написан (C/C++), указать критичность (Major), добавить CWE, которые находит детектор и дать краткое описание.

При запуске Svace запускает этот код и регистрирует новый тип предупреждения. Ко всем пользовательским предупреждениям добавляется префикс ‘USER.’, т.е. новое предупреждение, созданное выше, будет иметь тип USER.BAD_PRIVILEGE.

Для работы с предупреждениями у Svace есть утилита warning. Эта утилита также умеет работать с пользовательскими предупреждениями. Например, запуск утилиты для получения информации о предупреждении, выведет всю доступную информацию:

$ svace warning -i USER.BAD_PRIVILEGE
##############################
# Situation: Quality
# Reliability: Unknown
# Severity: Major
# Group: 
# Available languages: CXX
# Detection tools: SvaceIR-API
# CWEs: CWE266
# Description: Checker for incorrect use privileges by linux functions.
#
USER.BAD_PRIVILEGE = true

Light API

Light API предназначено для написания детекторов для поиска несложных паттернов в исходном коде.

Для создания детекторов Svace предоставляет интерфейс SvaceLightCheckerBuilder, который в настоящий момент предоставляет следующую функциональность:

Созданные паттерны запускаются во время работы основной фазы анализа, которая выполняет обход всех функций программы на основе графа вызовов. Если паттерн срабатывает, то будет выдано предупреждение.

Следующий код

@Override
public void createChecker(SvaceLightCheckerBuilder builder) {
	var mask = builder.hasBitmask(0777);
        var argPattern = builder.patternFuncArg(0, mask);
        var funcPattern = builder.patternFuncCall("chmod" ,argPattern);
        builder.registerChecker(warning, "Using mask 777 for 'chmod'", funcPattern);
}

создаёт детектор, который выдаст сообщение, если для функции chmod используется аргумент с маской 777.

IR API

Дополнительно мы предоставили API для доступа к внутреннему представлению программы анализаторов. Для регистрации необходимо реализовать метод newIRModule интерфейса SvaceIRPlugin.

Этот метод вызывается анализатором для каждого модуля анализируемой программы (модуль — единица компиляции). Анализатор поочерёдно предоставляет информацию о глобальных переменных и теле реализованных функций. Для каждой функции приводится список инструкций.

Следует заметить, что не вся информация в исходном коде будет присутствовать, а только та, что доступна анализатору. Например, декларации функций не включаются в наше внутреннее представление.

Этот интерфейс позволяет написать чуть более сложную логику для детекторов. Кроме этого можно создать плагин, который только логирует IR, и не выдаёт никаких предупреждений.


Алексей Бородин