Статический анализ кода

Стати́ческий ана́лиз ко́да (англ. 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].

Статический анализ постоянно применяется для критического ПО в следующих областях:

  1. ПО для медицинских устройств[4];
  2. ПО для атомных станций и систем защиты реактора (Reactor Protection Systems)[5];
  3. ПО для авиации (в комбинации с динамическим анализом)[6];
  4. ПО на автомобильном или железнодорожном транспорте[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++

C#

Java

JavaScript

.NET

PHP

  • PHPStan
  • Psalm
  • RIPS
  • YASCA
  • Visual Code Grepper
  • SonarQube

Python

  • Pylint
  • Pyflakes
  • Flake8
  • Pyright
  • mypy
  • Bandit[15]
  • McCabe
  • Solar appScreener

Ruby

  • Brakeman

Rust

  • clippy

Другие

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

См. также

Примечания

  1. Software Quality Objectives for Source Code. Proceedings Embedded Real Time Software and Systems 2010 Conference, ERTS2, Toulouse, France: Patrick Briand et al. PDF (архивировано)
  2. Да, PVS-Studio умеет выявлять утечки памяти / Блог компании PVS-Studio
  3. 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
  4. FDA. Infusion Pump Software Safety Research at FDA. Food and Drug Administration (8 сентября 2010). Дата обращения: 9 сентября 2010. Архивировано 1 сентября 2010 года.
  5. 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
  6. 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.»
  7. Bill Graham. Static Analysis, Safety-Critical Railway Software, and EN 50128. Дата обращения: 2 сентября 2016. Архивировано 25 августа 2016 года.
  8. VDC Research. Automated Defect Prevention for Embedded Software Quality. VDC Research (1 февраля 2012). Дата обращения: 10 апреля 2012. Архивировано 7 апреля 2012 года.
  9. John Barnes, «High Integrity Software: The SPARK Approach to Safety and Security», Addison-Wesley, 2003
  10. Daniel Jackson. Software Abstractions: Logic, Language, and Analysis. — MIT Press, 2012. — ISBN 978-0-262-51862-3.
  11. ТОП бесплатных инструментов для статического анализа кода. cisoclub.ru (11 февраля 2021). Дата обращения: 19 ноября 2021. Архивировано 19 сентября 2021 года.
  12. Clang Static Analyzer. clang-analyzer.llvm.org. Дата обращения: 14 мая 2016. Архивировано 8 октября 2011 года.
  13. Coverity Static Application Security Testing (SAST). Дата обращения: 15 мая 2025.
  14. SpotBugs. Дата обращения: 15 мая 2025.
  15. Bandit documentation. Дата обращения: 15 мая 2025.
  16. SonarQube - Static Code Analysis. Дата обращения: 15 мая 2025.

Ссылки