Стати́ческий ана́лиз ко́да () — анализ исходного кода программного обеспечения, производимый без реального выполнения исследуемых программ (в отличие от динамического анализа). В большинстве случаев анализ производится над исходным кодом, хотя иногда анализу подвергается объектный код, например P-код или код на MSIL. Термин обычно применяют к анализу, производимому специальным программным обеспечением (ПО), тогда как ручной анализ называют «program understanding» или «program comprehension» (пониманием программы).

В зависимости от используемого инструмента глубина анализа может варьироваться от определения поведения отдельных операторов до анализа всего имеющегося кода. Способы использования полученной в ходе анализа информации также различны — от выявления мест, возможно содержащих ошибки (утилиты типа Lint), до формальных методов, позволяющих математически доказать какие-либо свойства программы (например, соответствие поведения спецификации).

В некоторых источниках программные метрики и обратное проектирование рассматриваются как формы статического анализа. Получение метрик () и статический анализ часто совмещаются, особенно при создании встраиваемых системSoftware Quality Objectives for Source Code. Proceedings Embedded Real Time Software and Systems 2010 Conference, ERTS2, Toulouse, France: Patrick Briand et al. PDF (архивировано).

Современные средства анализа, такие как Coverity, SonarQube, PVS-Studio, Clang Static Analyzer и Infer от Meta, используют методы анализа потока данных, контроля потока управления, а также абстрактной интерпретации для выявления ошибок, неочевидных при компиляции. Многие инструменты интегрируются в CI/CD-конвейеры и автоматизированные процессы разработки программного обеспечения для раннего выявления уязвимостей и дефектов.

Статический анализ играет важную роль в обеспечении безопасной разработки, соответствующей таким стандартам, как OWASP и MISRA C (в автомобильной индустрии), и широко применяется при разработке критически важных систем.

Принципы статического анализа Примером простейшего статического анализа являются генерируемые большинством компиляторов (например, GCC) «предупреждения» () — диагностические сообщения о потенциальной ошибочности синтаксически правильного кода. Например, для следующего кода на 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, предоставляют различные уровни проверки, включая межпроцедурный анализ, анализ сложных выражений, контроль утечек памяти и другие формальные методыДа, PVS-Studio умеет выявлять утечки памяти / Блог компании PVS-Studio.

Применение В последнее время статический анализ всё больше используется в верификации свойств ПО, используемого в компьютерных системах высокой надёжности, особенно критичных для жизни (). Также применяется для поиска кода, потенциально содержащего уязвимости (иногда это применение называется Static Application Security Testing, SAST)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 .

Статический анализ постоянно применяется для критического ПО в следующих областях: ПО для медицинских устройств; ПО для атомных станций и систем защиты реактора (Reactor Protection Systems)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 ; ПО для авиации (в комбинации с динамическим анализом)Position Paper CAST-9. Considerations for Evaluating Safety Engineering Approaches to Software Assurance // 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.»; ПО на автомобильном или железнодорожном транспорте.

По данным VDC на 2012 год, примерно 28 % разработчиков встраиваемого ПО применяют средства статического анализа, а 39 % собираются начать их использование в течение 2 лет.

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

Примеры применения формальных методов:

Frama-C — платформа для анализа программ на языке C, использующая аннотации и абстрактную интерпретацию для доказательства корректности. SPARK — формальный диалект языка Ada, предназначенный для разработки критичных к безопасности систем. Используется, например, в авионике и управлении железнодорожными системамиJohn Barnes, «High Integrity Software: The SPARK Approach to Safety and Security», Addison-Wesley, 2003. Coq — интерактивная среда доказательств, применяемая для формальной верификации алгоритмов и программ. Используется в проектах типа CompCert, Bedrock и Verasco. Why3 — платформа для формальной спецификации и верификации программ, интегрирующая различные автоматические доказатели (Z3, Alt-Ergo и др.). Dafny — язык программирования с поддержкой формальной верификации. Компилятор может автоматически доказывать корректность кода относительно спецификации.

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

Инструменты статического анализа Существует множество инструментов статического анализа, охватывающих различные языки программирования. Ниже приведён перечень наиболее известных из них, основанный в том числе на публикации CISO CLUB и других источниках.

C/C++ BLAST Clang Static Analyzer (встроен в Clang) Coverity PC-Lint lint и lock_lint (Sun Studio) Cppcheck (официальный сайт)

PVS-Studio

(MathWorks)

Visual Code Grepper

C# CodeCrawler Visual Code Grepper PVS-Studio NDepend StyleCop ReSharper

Java FindBugs (официальный сайт) SpotBugs — форк FindBugs Infer (Facebook)

YASCA SonarQube

JavaScript JSLint ESLint SonarQube

.NET Roslyn (.NET Compiler Platform) FxCop NDepend StyleCop PVS-Studio

PHP PHPStan Psalm RIPS YASCA Visual Code Grepper SonarQube

Python Pylint Pyflakes Flake8 Pyright mypy Bandit McCabe Solar appScreener

Ruby Brakeman

Rust clippy

Другие SonarQube — многоплатформенный инструмент для анализа качества кода T-SQL Analyzer — анализатор SQL-скриптов и хранимых процедур для Microsoft SQL Server.

См. также Формальная верификация Тестирование программного обеспечения Безопасное программирование

MISRA C

Примечания

Ссылки О безошибочных программах // «Открытые системы», № 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)

Категория:Тестирование программного обеспечения Категория:Исследование программ Категория:Формальные методы Категория:Статические анализаторы кода Категория:Статьи с примерами кода C++