Статический анализ кода
Стати́ческий ана́лиз ко́да (англ. static code analysis) — анализ исходного кода программного обеспечения, производимый без реального выполнения исследуемых программ (в отличие от динамического анализа). В большинстве случаев анализ производится над исходным кодом, хотя иногда анализу подвергается объектный код, например P-код или код на MSIL. Термин обычно применяют к анализу, производимому специальным программным обеспечением (ПО), тогда как ручной анализ называют «program understanding» или «program comprehension» (пониманием программы).
В зависимости от используемого инструмента глубина анализа может варьироваться от определения поведения отдельных операторов до анализа всего имеющегося кода. Способы использования полученной в ходе анализа информации также различны — от выявления мест, возможно содержащих ошибки (утилиты типа Lint), до формальных методов, позволяющих математически доказать какие-либо свойства программы (например, соответствие поведения спецификации).
В некоторых источниках программные метрики и обратное проектирование рассматриваются как формы статического анализа. Получение метрик (англ. software quality objectives) и статический анализ часто совмещаются, особенно при создании встраиваемых систем[1].
Современные средства анализа, такие как Coverity, SonarQube, PVS-Studio, Clang Static Analyzer и Infer от Meta, используют методы анализа потока данных, контроля потока управления, а также абстрактной интерпретации для выявления ошибок, неочевидных при компиляции. Многие инструменты интегрируются в CI/CD-конвейеры и автоматизированные процессы разработки программного обеспечения для раннего выявления уязвимостей и дефектов.
Статический анализ играет важную роль в обеспечении безопасной разработки, соответствующей таким стандартам, как OWASP и MISRA C (в автомобильной индустрии), и широко применяется при разработке критически важных систем.
Принципы статического анализа
Примером простейшего статического анализа являются генерируемые большинством компиляторов (например, GCC) «предупреждения» (англ. warnings) — диагностические сообщения о потенциальной ошибочности синтаксически правильного кода. Например, для следующего кода на C может быть получено сообщение о неинициализированной переменной:
int x;
int y = x + 2; // Переменная x не инициализирована!
В связи с высокими требованиями к скорости компиляции и качеству машинного кода, компиляторы проверяют код лишь на простейшие ошибки. Статические анализаторы предназначены для более детального исследования кода, включая межпроцедурный анализ, определение утечек памяти, проблем конкурентности и возможных уязвимостей.
Типы ошибок, обнаруживаемых статическими анализаторами
Статические анализаторы выявляют широкий спектр ошибок, которые могут привести к сбоям, уязвимостям и проблемам сопровождения программного обеспечения:
- Неопределённое поведение — например, использование неинициализированных переменных, обращение к нулевым указателям. Простые случаи могут быть обнаружены ещё на этапе компиляции.
- Нарушения контракта использования библиотек. Например, в языках с управлением файлами каждый вызов открытия файла должен сопровождаться вызовом его закрытия. Если файловая переменная теряется до закрытия, это может быть отмечено как ошибка.
- Недокументированное поведение, возникающее при использовании конструкций, выходящих за рамки спецификации языка.
- Переполнение буфера, особенно при использовании небезопасных функций (например,
strcpy,sprintf) или при ошибках работы с массивами и указателями. - Ошибки кроссплатформенности. Например, приведение указателей к типу
intможет работать на 32-битных архитектурах, но потерять значимые данные на 64-битных:
Object *p = getObject();
int pNum = reinterpret_cast<int>(p); // часть указателя теряется на x64
- Копипаст-ошибки в повторяющемся коде — например, неправильное копирование строк с небольшими изменениями:
dest.x = src.x + dx;
dest.y = src.y + dx; // ошибка: дублируется dx, а не dy
- Ошибки форматных строк, особенно в языках со статической типизацией. Например:
std::wstring s;
printf("s is %s and d is %d", s);
- Неиспользуемые или неизменные параметры, которые могут указывать на ошибки проектирования или устаревшую логику:
void doSomething(bool flag) // flag всегда равен true
{
if (flag)
// какая-то логика
else
// код не используется
}
doSomething(true);
- Утечки памяти и ресурсов. Несмотря на то, что динамические анализаторы лучше подходят для выявления таких проблем, статические инструменты также могут обнаруживать ошибки:
Traverser *t = new Traverser(Name);
if (!t->Valid())
{
return FALSE; // выход без delete
delete t;
}
- Вызовы функций без побочных эффектов без использования возвращаемого значения:
std::string s;
s.empty(); // вызов ничего не делает; возможно, имелось в виду s.clear()
- Прочие ошибки, зависящие от конкретного контекста и языка программирования.
Инструменты, такие как PVS-Studio, Coverity и Clang Static Analyzer, предоставляют различные уровни проверки, включая межпроцедурный анализ, анализ сложных выражений, контроль утечек памяти и другие формальные методы[2].
Применение
В последнее время статический анализ всё больше используется в верификации свойств ПО, используемого в компьютерных системах высокой надёжности, особенно критичных для жизни (safety-critical). Также применяется для поиска кода, потенциально содержащего уязвимости (иногда это применение называется Static Application Security Testing, SAST)[3].
Статический анализ постоянно применяется для критического ПО в следующих областях:
- ПО для медицинских устройств[4];
- ПО для атомных станций и систем защиты реактора (Reactor Protection Systems)[5];
- ПО для авиации (в комбинации с динамическим анализом)[6];
- ПО на автомобильном или железнодорожном транспорте[7].
По данным VDC на 2012 год, примерно 28 % разработчиков встраиваемого ПО применяют средства статического анализа, а 39 % собираются начать их использование в течение 2 лет[8].
Формальные методы
Формальные методы — это строго определённые математические подходы, применяемые для доказательства корректности программного обеспечения на основе его спецификаций. В рамках статического анализа кода формальные методы позволяют не только находить ошибки, но и доказывать свойства программ, включая отсутствие определённых классов ошибок, таких как переполнение буфера, гонки данных или утечки памяти.
Примеры применения формальных методов:
- Frama-C — платформа для анализа программ на языке C, использующая аннотации и абстрактную интерпретацию для доказательства корректности.
- SPARK — формальный диалект языка Ada, предназначенный для разработки критичных к безопасности систем. Используется, например, в авионике и управлении железнодорожными системами[9].
- Coq — интерактивная среда доказательств, применяемая для формальной верификации алгоритмов и программ. Используется в проектах типа CompCert, Bedrock и Verasco.
- Why3 — платформа для формальной спецификации и верификации программ, интегрирующая различные автоматические доказатели (Z3, Alt-Ergo и др.).
- Dafny — язык программирования с поддержкой формальной верификации. Компилятор может автоматически доказывать корректность кода относительно спецификации.
Формальные методы находят применение при разработке систем, критичных к безопасности, таких как авионика, медицинские устройства, автомобильные контроллеры и банковские системы. Несмотря на высокую стоимость внедрения, они позволяют существенно снизить риски, связанные с программными ошибками[10].
Инструменты статического анализа
Существует множество инструментов статического анализа, охватывающих различные языки программирования. Ниже приведён перечень наиболее известных из них, основанный в том числе на публикации CISO CLUB[11] и других источниках.
C/C++
- BLAST
- Clang Static Analyzer (встроен в Clang)[12]
- Coverity[13]
- PC-Lint
- lint и lock_lint (Sun Studio)
- Cppcheck (официальный сайт)
- Parasoft C/C++Test
- PVS-Studio
- LDRA Testbed
- Polyspace (MathWorks)
- QA-C
- Cantata++
- Visual Code Grepper
C#
- CodeCrawler
- Visual Code Grepper
- PVS-Studio
- NDepend
- StyleCop
- ReSharper
Java
- FindBugs (официальный сайт)
- SpotBugs — форк FindBugs[14]
- Infer (Facebook)
- Parasoft JTest
- RIPS Technologies
- YASCA
- SonarQube
JavaScript
.NET
- Roslyn (.NET Compiler Platform)
- FxCop
- NDepend
- StyleCop
- PVS-Studio
PHP
- PHPStan
- Psalm
- RIPS
- YASCA
- Visual Code Grepper
- SonarQube
Python
Ruby
- Brakeman
Rust
- clippy
Другие
См. также
- Формальная верификация
- Тестирование программного обеспечения
- Безопасное программирование
- Список средств статического анализа кода
- MISRA C
Примечания
- ↑ Software Quality Objectives for Source Code. Proceedings Embedded Real Time Software and Systems 2010 Conference, ERTS2, Toulouse, France: Patrick Briand et al. PDF (архивировано)
- ↑ Да, PVS-Studio умеет выявлять утечки памяти / Блог компании PVS-Studio
- ↑ Improving Software Security with Precise Static and Runtime Analysis, Benjamin Livshits, section 7.3 "Static Techniques for Security, " Stanford doctoral thesis, 2006. http://research.microsoft.com/en-us/um/people/livshits/papers/pdf/thesis.pdf Архивная копия от 5 июня 2011 на Wayback Machine
- ↑ FDA. Infusion Pump Software Safety Research at FDA. Food and Drug Administration (8 сентября 2010). Дата обращения: 9 сентября 2010. Архивировано 1 сентября 2010 года.
- ↑ Computer based safety systems — technical guidance for assessing software aspects of digital computer based protection systems, http://www.hse.gov.uk/nuclear/operational/tech_asst_guides/tast046.pdf Архивная копия от 9 октября 2012 на Wayback Machine
- ↑ Position Paper CAST-9. Considerations for Evaluating Safety Engineering Approaches to Software Assurance Архивная копия от 6 октября 2013 на Wayback Machine // FAA, Certification Authorities Software Team (CAST), January, 2002: «Verification. A combination of both static and dynamic analyses should be specified by the applicant/developer and applied to the software.»
- ↑ Bill Graham. Static Analysis, Safety-Critical Railway Software, and EN 50128. Дата обращения: 2 сентября 2016. Архивировано 25 августа 2016 года.
- ↑ VDC Research. Automated Defect Prevention for Embedded Software Quality. VDC Research (1 февраля 2012). Дата обращения: 10 апреля 2012. Архивировано 7 апреля 2012 года.
- ↑ John Barnes, «High Integrity Software: The SPARK Approach to Safety and Security», Addison-Wesley, 2003
- ↑ Daniel Jackson. Software Abstractions: Logic, Language, and Analysis. — MIT Press, 2012. — ISBN 978-0-262-51862-3.
- ↑ ТОП бесплатных инструментов для статического анализа кода. cisoclub.ru (11 февраля 2021). Дата обращения: 19 ноября 2021. Архивировано 19 сентября 2021 года.
- ↑ Clang Static Analyzer. clang-analyzer.llvm.org. Дата обращения: 14 мая 2016. Архивировано 8 октября 2011 года.
- ↑ Coverity Static Application Security Testing (SAST). Дата обращения: 15 мая 2025.
- ↑ SpotBugs. Дата обращения: 15 мая 2025.
- ↑ Bandit documentation. Дата обращения: 15 мая 2025.
- ↑ SonarQube - Static Code Analysis. Дата обращения: 15 мая 2025.
Ссылки
- О безошибочных программах // «Открытые системы», № 07, 2004
- Первые шаги к решению проблемы верификации программ // «Открытые системы», № 08, 2006
- Статический анализ безопасности кода // Программная инженерия и информационная безопасность. 2013 № 1, стр 50 (npo-echelon.ru)
- http://www.drdobbs.com/testing/deploying-static-analysis/240003801
- Коллекция ошибок, обнаруженных в Open Source проектах с помощью статического анализа кода // PVS-Studio (viva64.com)