Статический анализ кода
13.12.2013 23:31:04Некоторое время назад прикрутили к рабочим сборкам статический анализ кода с помощью Clang. В силу специфики собираемого это заняло определённое время, однако нынче всё работает автомагическим образом и еженощно выдаёт результаты анализа всего-всего, что собирается в продукте. Сама сборка, уже в силу специфики сборочной среды, при этом, осталась на GCC, анализ прикручен сбоку.
Разумеется, нашлось много интересного.
Конечно, наибольшее внимание было к чисто нашему коду, и интересно, что ожидания совпали с результатом. Одна пара пакетов, относительно которой изначально были сомнения, исходя из того, сколько приходилось их править в этом году, показала заметное количество ошибок, а вот остальные оказались в очень хорошем состоянии, поправили мелочи, в основном.
Разумеется, также мы активно используем и ёпен сорс. Что-то «как есть», что-то немного дорабатывается, что-то активно и постоянно.
В принципе, взаимодействие с ёпен сорсом учит тому, что качество кода в нём может, так сказать, сильно варьироваться. Как правило, поддерживаемый коммерчески ёпен сорс достаточно качественен, остальное, с той же вероятностью, является страшным шлаком. Из этого правила есть и исключения, причём, в обе стороны, иногда и коммерчески разрабатываемый ёпен сорс является страшным шлаком, а сделанный в свободное время, но квалифицированным человеком и с любовью, компонент оказывается отличного качества.
Прогон анализатора по нашей сборке (немногим более 200 пакетов) подтвердил как правило, так и исключения. На данный момент из общего количества лишь 34 пакета не имеют никаких обнаруженных ошибок. Порядка 50 с лишним укладываются всего в в пару ошибок.
Ошибки анализатора в конфигурации по умолчанию делятся на семь категории: три класса ошибок API (дженериковый, юниксовый и сишный; различные проверки передаваемых параметров в штатные функции), ошибки безопасности (самое распространённое — отсутствие проверки кода возврата функций, меняющих uid/gid и т.п.), а также ошибки в работе с памятью (переполнения, use after free(), утечки), ошибки логики (разыменование NULL, использование неинициализированных переменных и т.п.), и, так называемые, мертвяки (присвоения, наращения и прочие манипуляции с переменными, результат которых далее никак не используется).
Самые интересные классы ошибок, конечно, это то, что касается логики и памяти. Далее уже безопасность и разные API. Мертвый код тоже интересный показатель, но в самую последнюю очередь.
По-настоящему удивить смог Интел, абсолютно все их драйвера (e1000e, igb, ixgbe, они тоже входят в сборку) показали идеальный результат (ноль ошибок) с первого же прогона. На мой взгляд, это достаточно чётко показывает, что Интел использует инструменты статического анализа внутри, поскольку иначе, учитывая объём кода, а также дикое количество ифдефов для разных версий ядер, должно было найтись хоть что-нибудь, мёртвый код, например. Или даже ошибочная ошибка, но нет и такого.
Более 50 пакетов показывают одну-две ошибки, то есть практически чисты. Разумеется, есть и ошибки анализатора, но их количество также зависит от конкретного пакета, где-то находится всего пара ошибок, но обе стопроцентно по делу, а где-то из трёх десятков реальных всего 5.
Из интересных наблюдений — expat показывает всего две ошибки, тогда как libxml2 116, zlib одну, а bzip2 уже шесть, сборка perl даёт 260 ошибок, а вот python 78. Даже с учётом множественных ошибок анализатора, верхнеуровневое представление о качестве кода это даёт, так что инструмент полезен ещё и в этом смысле. Ещё более он полезен с ошибками реальными, недавно lighttpd правил CVE-2013-4559 и CVE-2013-4560, которые были найдены именно анализатором Clang.
У анализатора есть один интересный параметр, max-loops, количество итераций по циклам, по умолчанию равен четырём. Его увеличение, вопреки моим ожиданиям, не привело к тупому увеличению количества ошибок, а именно что уточнило результат, то есть, где-то (и довольно много где) ошибки снялись, а где-то, наоборот, появились. Параметр, очевидно, весьма полезный, но в нашей сборке больше 16, увы, выставить не получается, на 32 сборка не заканчивается даже за 8 часов.
Я считаю, что такого рода инструмент обязателен к использованию, поскольку он обладает двумя важными свойствами — его использование ничего не стоит (достаточно один раз накрутить автоматическую сборку, причём, она она не обязана быть релизной) и он может находить реальные проблемы. То есть, соотношение результата и затрат очень приятное.
По этой же причине, было бы неплохо, чтобы использование такого инструмента стало правилом хорошего тона в ёпен сорсе. Однако, учитывая суровые ёпен сорсные реалии, повсеместно такого ожидать не приходится. Единственная надежда на то, что кто-нибудь прикрутит анализатор к сборкам пакетов какого-нибудь Дебьяна, тем более, что даже просто сборка уже даёт интересные результаты, просто в силу того, что кланг более суров к некоторым типам ошибок, ну и, плюс, не поддерживает некоторые (сомнительные) расширения GCC.
В общем, рекомендую.