Статический анализ защищает ваш код от бомб замедленного действия
Статический анализ кода позволяет выявлять и устранять многие дефекты на раннем этапе. Более того, можно обнаружить спящие ошибки, которые в момент появления никак не проявляют себя. Они могут доставить массу проблем в будущем, потребовав для своего обнаружения многих часов отладки. Рассмотрим пример такой спящей ошибки.
Чтобы показать преимущество регулярного использования статического анализатора PVS-Studio, мы настроили у себя регулярную проверку проекта Blender. Подробнее про эту идею мой коллега писал здесь.
Я временами посматриваю на предупреждения, сгенерированные для нового или изменённого кода Blender. Новые ошибки находятся регулярно, но большинство из них скучные или несущественные. Я, как рыбак, сижу и жду, когда будет что-то интересное, про что захочется написать. Сегодня как раз такой случай.
void UI_but_drag_set_asset(uiBut *but,
const AssetHandle *asset,
const char *path,
int import_type,
int icon,
struct ImBuf *imb,
float scale)
{
....
asset_drag->asset_handle = MEM_mallocN(sizeof(asset_drag->asset_handle),
"wmDragAsset asset handle");
*asset_drag->asset_handle = *asset;
....
}
Код должен выделить буфер в памяти, достаточный для хранения структуры типа AssetHandle. Такова была задумка программиста. Но на самом деле он выделяет буфер, равный не размеру структуры, а размеру указателя.
Ошибка здесь:
sizeof(asset_drag->asset_handle)
Правильный вариант:
sizeof(*asset_drag->asset_handle)
Анализатор выявил эту ошибку, выдав предупреждение: V568: It’s odd that 'sizeof ()' operator evaluates the size of a pointer to a class, but not the size of the 'asset_drag→asset_handle' class object. interface.c 6192
Всё просто. Классический паттерн ошибки, с которым мы встречались в различных проектах. Интересно другое! Этот код сейчас работает правильно! Автору кода повезло. Посмотрим, что собой представляет структура AssetHandle:
typedef struct AssetHandle {
const struct FileDirEntry *file_data;
} AssetHandle;
Структура сейчас содержит в себе ровно один указатель. Получается, что размер структуры совпадает с размером указателя!
Перед нами красивая бомба замедленного действия. Этот код будет надёжно и стабильно работать годами. Он будет полностью работоспособен до тех пор, пока кто-то не захочет добавить в структуру новое поле.
В этот момент приложение сломается. Причём будет непонятно, что и где именно сломалось. Под структуру будет выделяться памяти меньше, чем требуется. Хорошо если программисту повезёт и при выходе за границу буфера возникнет Access Violation. Но, скорее всего, просто будет портиться какая-то память, и разработчика могут ждать часы мучительной отладки кода.
Используйте статический анализ кода, чтобы существенно улучшить качество и надёжность кода. Это полезно как в краткосрочной, так и в долгосрочной перспективе.
Статический анализ способен выявить не все ошибки. Однако преимуществ от его регулярного использования больше, чем затрат на ежедневный просмотр отчёта с новыми предупреждениями. Недавно в статье наш пользователь как раз описывал, как он пришёл к выводу, что лучше запускать анализатор, чем три дня отлаживаться.
Если хотите поделиться этой статьей с англоязычной аудиторией, то прошу использовать ссылку на перевод: Andrey Karpov. Static analysis protects your code from time bombs.