Анализатор PVS-Studio всегда умел искать множество различных дефектов безопасности (потенциальных уязвимостей) в коде программ. Однако, исторически сложилось, что мы позиционировали PVS-Studio как инструмент для поиска ошибок. Сейчас наблюдается мода на поиск в коде именно уязвимостей, а не ошибок, хотя на самом деле это одно и тоже. Что же, значит пришло время провести ребрендинг нашего статического анализатора PVS-Studio. Начнём мы с Common Weakness Enumeration (CWE). В этой статье приводится таблица, сопоставляющая диагностические предупреждения PVS-Studio с классификатором. Таблица будет постепенно пополняться и изменяться, но уже сейчас с её помощью мы сможем писать статьи, посвященные обнаруженным дефектам безопасности в том или ином проекте. Думаем, это привлечёт к нашему инструменту больше внимания специалистов, занимающихся безопасностью программного обеспечения.

Common Weakness Enumeration (CWE)


Для начала давайте разберемся с терминологией. Для этого я процитирую фрагмент FAQ с сайта cwe.mitre.org.

A1. Что такое CWE? Что такое «дефект безопасности ПО»?

Общий перечень дефектов безопасности ПО (Common Weakness Enumeration, CWE) предназначен для разработчиков и специалистов по обеспечению безопасности ПО. Он представляет собой официальный реестр или словарь общих дефектов безопасности, которые могут проявиться в архитектуре, проектировании, коде или реализации ПО, и могут быть использованы злоумышленниками для получения несанкционированного доступа к системе. Данный перечень был разработан в качестве универсального формального языка для описания дефектов безопасности ПО, а также в качестве стандарта для измерения эффективности инструментов, выявляющих такие дефекты, и для распознавания, устранения и предотвращения этих дефектов.

Дефекты безопасности ПО — это дефекты, сбои, ошибки, уязвимости и прочие проблемы реализации, кода, проектирования или архитектуры ПО, которые могут сделать системы и сети уязвимыми к атакам злоумышленников, если их вовремя не исправить. К таким проблемам относятся: переполнения буферов, ошибки форматной строки и т.д.; проблемы структуры и оценки валидности данных; манипуляции со специальными элементами; ошибки каналов и путей; проблемы с обработчиками; ошибки пользовательского интерфейса; обход каталога и проблемы с распознаванием эквивалентности путей; ошибки аутентификации; ошибки управления ресурсами; недостаточный уровень проверки данных; проблемы оценки входящих данных и внедрение кода; проблемы предсказуемости и недостаточная «случайность» случайных чисел.

A2. В чем разница между уязвимостью и дефектом безопасности ПО?

Дефекты безопасности — это ошибки, которые могут спровоцировать уязвимости. Уязвимости, например, описанные в перечне общих уязвимостей и подверженностей воздействиям (Common Vulnerabilities and Exposures, CVE), — это ошибки программы, которые могут быть непосредственно использованы злоумышленником для получения доступа к системе или сети.

Соответствие между предупреждениями PVS-Studio и CWE


Нам хочется, чтобы анализатор PVS-Studio начали воспринимать не только как инструмент поиска ошибок, но и как инструмент, который помогает сократить количество уязвимостей в коде. Конечно, не каждый дефект безопасности, перечисленный в CWE, является уязвимостью. Можно ли использовать тот или иной дефект для атаки, зависит от множества факторов. Поэтому в дальнейшем мы будем писать, что анализатор PVS-Studio выявляет не уязвимости, а потенциальные уязвимости. Это будет более правильно.

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

CWE PVS-Studio CWE Description
CWE-14 V597 Compiler Removal of Code to Clear Buffers
CWE-121 V755 Stack-based Buffer Overflow
CWE-122 V755 Heap-based Buffer Overflow
CWE-123 V575 Write-what-where Condition
CWE-129 V557, V781, V3106 Improper Validation of Array Index
CWE-131 V514, V531, V568, V620, V627, V635, V641, V651, V687, V706, V727 Incorrect Calculation of Buffer Size
CWE-134 V576, V618, V3025 Use of Externally-Controlled Format String
CWE-135 V518, V635 Incorrect Calculation of Multi-Byte String Length
CWE-188 V557, V3106 Reliance on Data/Memory Layout
CWE-195 V569 Signed to Unsigned Conversion Error
CWE-197 V642 Numeric Truncation Error
CWE-36 V631, V3039 Absolute Path Traversal
CWE-369 V609, V3064 Divide By Zero
CWE-401 V701, V773 Improper Release of Memory Before Removing Last Reference ('Memory Leak')
CWE-404 V611, V773 Improper Resource Shutdown or Release
CWE-415 V586 Double Free
CWE-416 V774 Use after free
CWE-457 V573, V614, V670, V3070, V3128 Use of Uninitialized Variable
CWE-462 V766, V3058 Duplicate Key in Associative List (Alist)
CWE-467 V511, V512, V568 Use of sizeof() on a Pointer Type
CWE-468 V613, V620, V643 Incorrect Pointer Scaling
CWE-476 V522, V595, V664, V757, V769, V3019, V3042, V3080, V3095, V3105, V3125 NULL Pointer Dereference
CWE-478 V577, V719, V622, V3002 Missing Default Case in Switch Statement
CWE-481 V559, V3055 Assigning instead of comparing
CWE-482 V607 Comparing instead of Assigning
CWE-483 V640, V3043 Incorrect Block Delimitation
CWE-561 V551, V695, V734, V776, V779, V3021 Dead Code
CWE-562 V558 Return of Stack Variable Address
CWE-563 V519, V603, V751, V763, V3061, V3065, V3077, V3117 Assignment to Variable without Use ('Unused Variable')
CWE-570 V501, V547, V560, V654, V3022, V3063 Expression is Always False
CWE-571 V501, V547, V560, V617, V654, V694, V3022, V3063 Expression is Always True
CWE-587 V566 Assignment of a Fixed Address to a Pointer
CWE-588 V641 Attempt to Access Child of a Non-structure Pointer
CWE-674 V3110 Uncontrolled Recursion
CWE-690 V522, V3080 Unchecked Return Value to NULL Pointer Dereference
CWE-762 V611 Mismatched Memory Management Routines
CWE-805 V512, V594, V3106 Buffer Access with Incorrect Length Value
CWE-806 V512 Buffer Access Using Size of Source Buffer
CWE-843 V641 Access of Resource Using Incompatible Type ('Type Confusion')
Таблица N1. Первый черновой вариант таблицы соответствий CWE и диагностик PVS-Studio.

Теперь мы сможем писать в статьях о проверке проектов, какие мы нашли потенциальные уязвимости в том или ином проекте. Раз многие хвалятся, что их анализаторы выявляют дефекты безопасности, то и мы затронем эту тему в своих статьях.

Демонстрация


Давайте посмотрим, как приведенную выше таблицу нам можно использовать при написании статей. Проанализируем проект и посмотрим на диагностические сообщения PVS-Studio с точки зрения дефектов безопасности.

Конечно, далеко не каждый проект стоит изучать с точки зрения уязвимости. Поэтому давайте возьмем такой серьезный проект, как Apache HTTP Server.

Итак, проверяем Apache HTTP Server с помощью PVS-Studio и видим, что баги лезут из всех щелей. Стоп! Теперь это не баги, а дефекты безопасности! Намного солидней говорить про потенциальные уязвимости, чем про опечатки и ошибки.

Сразу скажу, что в этот раз мы не будем анализировать проект целиком, так как перед нами стоит задача только показать использование таблицы на практике. Остановимся на трех предупреждениях.

Пример N1
#define myConnConfig(c) (SSLConnRec *)ap_get_module_config(c->conn_config, &ssl_module)

....

int ssl_callback_alpn_select(SSL *ssl,
  const unsigned char **out, unsigned char *outlen,
  const unsigned char *in, unsigned int inlen,
  void *arg)
{
  conn_rec *c = (conn_rec*)SSL_get_app_data(ssl);
  SSLConnRec *sslconn = myConnConfig(c);
  apr_array_header_t *client_protos;
  const char *proposed;
  size_t len;
  int i;

  /* If the connection object is not available,
   * then there's nothing for us to do. */
  if (c == NULL) {
    return SSL_TLSEXT_ERR_OK;
  }
  ....
}

Анализатор PVS-Studio выдаёт предупреждение: V595 The 'c' pointer was utilized before it was verified against nullptr. Check lines: 2340, 2348. ssl_engine_kernel.c 2340

С точки зрения дефектов безопасности это: CWE-476 (NULL Pointer Dereference)

Суть ошибки. Выделим две наиболее важные сточки кода:
SSLConnRec *sslconn = myConnConfig(c);
if (c == NULL) {

Проверка (c == NULL) говорит нам, что указатель может быть нулевым. Однако, он уже разыменовывался внутри макроса myConnConfig:
#define myConnConfig(c) (SSLConnRec *)ap_get_module_config(c->conn_config, &ssl_module)

Таким образом, код никак не защищён от разыменовывания нулевого указателя.

Пример N2
int get_password(struct passwd_ctx *ctx)
{
  char buf[MAX_STRING_LEN + 1];
  ....
  memset(buf, '\0', sizeof(buf));
  return 0;
err_too_long:
  ....
}

Анализатор PVS-Studio выдаёт предупреждение: V597 The compiler could delete the 'memset' function call, which is used to flush 'buf' buffer. The memset_s() function should be used to erase the private data. passwd_common.c 165

С точки зрения дефектов безопасности это: CWE-14 (Compiler Removal of Code to Clear Buffers)

Суть ошибки. При компиляции кода в режиме оптимизации, компилятор удалит вызов функции memset, так как с точки зрения компилятора этот вызов лишний. После заполнения нулями буфера, созданного на стеке, этот буфер более никак не используется. Значит, заполнять буфер нулями — это пустая трата времени и следует удалить вызов функции memset. Таким образом, приватные данные не будут затерты и останутся в памяти.

Хочу обратить внимание, что это не абстрактное теоретически возможное поведение компилятора. Компиляторы действительно так делают, чтобы ускорить наши программы. Подробности:


Пример N3
static int is_quoted_pair(const char *s)
{
    int res = -1;
    int c;

    if (((s + 1) != NULL) && (*s == '\\')) {
        c = (int) *(s + 1);
        if (apr_isascii(c)) {
            res = 1;
        }
    }
    return (res);
}

Анализатор PVS-Studio выдаёт предупреждение: V694 The condition ((s + 1) != ((void *) 0)) is only false if there is pointer overflow which is undefined behaviour anyway. mod_mime.c 531

С точки зрения дефектов безопасности это: CWE-571 (Expression is Always True)

Суть ошибки. Условие ((s + 1) != NULL) всегда истинно. Ложным оно может стать только при переполнении указателя. Переполнение указателя приводит к неопределённому поведению программы, поэтому про такой случай говорить вообще смысла нет. Можно считать, что условие всегда истинно, о чем и сообщил нам анализатор.

Мы не авторы кода и точно не знаем, как должен выглядеть код, но, скорее всего, он должен быть таким:

if ((*(s + 1) != '\0') && (*s == '\\')) {

Заключение


Ура, анализатор PVS-Studio может использоваться для выявления потенциальных уязвимостей кода!

Предлагаем всем желающим подробнее познакомиться с анализатором кода PVS-Studio и попробовать демонстрационную версию анализатора на собственных проектах. Страница продукта: PVS-Studio.

По всем техническим вопросам и вопросам лицензирования просим писать нам на почту support [@] viva64.com или воспользоваться формой обратной связи.



Если хотите поделиться этой статьей с англоязычной аудиторией, то прошу использовать ссылку на перевод: Andrey Karpov, Phillip Khandeliants. PVS-Studio: searching software weaknesses

Прочитали статью и есть вопрос?
Часто к нашим статьям задают одни и те же вопросы. Ответы на них мы собрали здесь: Ответы на вопросы читателей статей про PVS-Studio, версия 2015. Пожалуйста, ознакомьтесь со списком.
Поделиться с друзьями
-->

Комментарии (17)


  1. Andrey2008
    16.03.2017 19:35

    Тишина. Тогда сам комментарий оставлю. :)

    Ещё примеры поиска уязвимостей в коде FreeBSD:

    1. CWE-467
    2. CWE-570
    3. CWE-571
    4. CWE-571
    5. CWE-571
    6. CWE-476
    7. CWE-563
    8. CWE-563
    9. CWE-561
    10. CWE-561


    1. alan008
      16.03.2017 20:05

      Андрей, я не C-разработчик, но не могли бы вы пояснить, чтобы злоумышленнику использовать любую из этих т.н. "уязвимостей", что нужно сделать? А то вы сейчас тут приучите народ после каждой строчки кода по 100 проверок добавлять, дабы не породить некую "дыру" в программе.


      1. Andrey2008
        16.03.2017 20:23

        Данные ошибки ещё не подтвержденные уязвимости (CVE), а только потенциальные уязвимости. Можно их использовать или нет — это сложная исследовательская задача, интересная не нам пользователям, а злоумышленникам. Нам же пользователям/разработчикам интересно побыстрее исправить эти ошибки от греха подальше.

        Про проверки. Да, где-то их может не хватать. Но, например, всё перечисленное здесь возникло не из-за отсутствия проверок, а из-за ошибок в коде.

        Что нужно делать? Это целый комплекс мер, который тянет на книгу. Но точно могу сказать, одна из мер, это использование статических анализаторов кода, таких как PVS-Studio. :)


        1. alan008
          16.03.2017 21:00

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


          1. Andrey2008
            16.03.2017 21:14

            предупреждения о неэффективном коде

            Такие предупреждения уже есть, хотя их пока немного. Мы называем их микро-оптимизации.


            1. alan008
              17.03.2017 10:47

              Андрей, понимаю вашу специфику (в большей степени поиск ошибок в низкоуровневом коде относительно высокого качества), но может вам замахнуться на всякие Python, Ruby, Java, PHP, Objective C — вот где непаханое поле багов и тормозов :-)
              Конечно, понятно, что это будет уже совсем другой продукт. Может и не стоит так распыляться.


              1. Andrey2008
                17.03.2017 10:56

                Пока мы считаем, что не готовы идти дальше. Если пойдём, то скорее всего это будет Java.


                1. alan008
                  17.03.2017 11:07

                  Да, это логично. Enterprise все-таки. Переход к языкам с динамической типизацией может потребовать пересмотра всех подходов к анализу.
                  Основная проблема добавления анализа другого языка — найти экспертов, которые знают, какие там бывают проблемы. Потому что надо сначала самому нарваться несколько раз на ошибку/проблему, чтобы понять, что её стОит выявлять. Желаю удачи в полезном деле! Кстати в качестве вашей рекламы не пробовали получить реальный отзыв от какого-то вашего клиента (не важно платного или бесплатно). Т.е. чтобы не вы сами, а ваши пользователи описали, какой профит они получили от PVS-Studio?


                  1. EvgeniyRyzhkov
                    17.03.2017 11:10

                    Пробовали. Вот, например, про Unreal Engine компании Epic Games.


                    1. alan008
                      17.03.2017 17:37

                      Ну не надо лукавить, здесь, я так понял, вы сами проверили, сами пофиксили и попросили их отписаться.


                      After publishing every new article about checking some project, people will ask: "Have you reported the bugs to the project authors?" And of course we always do! But this time, we've not only "reported the bugs to the authors" but fixed all those bugs ourselves.

                      Было бы интереснее услышать тех, кто добровольно сам пользуется и сам фиксит (не команда PVS). Но кто же захочет писать про свои баги. Хотя..


                      1. EvgeniyRyzhkov
                        17.03.2017 17:39

                        Очень мало кто готов публично писать о исправлении собственных ошибок. Причина — а вдруг наши клиенты подумают, что у нас в программах есть ошибки!

                        Это смешно звучит для программистов, но для директоров это вот так.


                      1. SvyatoslavMC
                        17.03.2017 18:01

                        Взгляните на статью Aurelien Aptel, он сам проверял и сам провил проект Samba. Статья из его блога: http://emacsdump.blogspot.ru/2016/03/running-pvs-studio-on-samba.html


  1. Andrey2008
    16.03.2017 23:12

    Почему полезно использовать PVS-Studio? Потому, что он находит неинициализированные переменные в LLVM! Use of Uninitialized Variable (CWE-457).


    1. EvgeniyRyzhkov
      16.03.2017 23:43
      +1

      Разве в Clang нет соответсвующей диагностики?


      1. Andrey2008
        17.03.2017 10:01

        Не знаю, не смотрел. Одну из двух. Или они это не осиливают. Или сами собой не пользуются. :)

        В целом же ничего удивительно. Задача PVS-Studio в том и состоит, чтобы быть лучше компиляторов в плане поиска дефектов.


        1. Andrey2008
          17.03.2017 10:05

          Мы и в плане C# сильны. Очередное доброе дело: EntityFramework. PVS-Studio: fixed vulnerability CWE-670 (Always-Incorrect Control Flow Implementation)


          1. lany
            18.03.2017 07:55
            +3

            В первом случае ошибка в юнит-тесте, который проверял меньше, чем следовало. Во втором — ошибка, которая ведёт к снижению производительности через увеличение коллизий хэшкодов, но на корректность не влияет. Доброе дело, да, но вряд ли это прямо fixed vulnerability :-) А уж рекламку вставлять в юнит-тест совсем как-то некрасиво, на мой взгляд. Хватило бы и commit-message.