# V2021. Using assertions may cause the abnormal program termination in undesirable contexts.

Данное диагностическое правило добавлено по просьбе пользователей.

Диагностическое правило позволяет обнаружить в коде вызов макроса, который может привести к аварийному завершению программы. Таким макросом может быть стандартный 'assert'. Несмотря на то, что его использование позволяет устранять ошибки и снизить вероятность возникновения уязвимостей, его вызов может быть недопустим в различных сценариях. Одним из таких вариантов может быть написание библиотечного кода.

Рассмотрим следующий код:

    [[noreturn]] void assertHandler();
    #define ASSERT(expr) (!!(expr) || (assertHandler(), 0))

    void foo(int i)
    {
      if (i < 0)
      {
        ASSERT(false && "The 'i' parameter must be non-negative");
      }
    }

В примере приведена пользовательская реализация макроса 'assert', которая вызывает функцию, не возвращающую поток управления вызывающей функции. Требуется, чтобы анализатор выдал срабатывание на вызов этого макроса. Для этого надо внести небольшие изменения в код:

    [[noreturn]] void assertHandler();                      // N1
    #define ASSERT(expr) (!!(expr) || (assertHandler(), 0))

    //V_PVS_ANNOTATIONS annotations.json                    // N2
    //V_ASSERT_CONTRACT, assertMacro:ASSERT                 // N3

    void foo(int i)
    {
      if (i < 0)
      {
        ASSERT(false);                                      // <= V2021
      }
    }

На этом примере рассмотрим, как настраивается механизм распознавания пользовательского макроса.

Разметка функции как noreturn. Функция 'assertHandler', расположенная внутри макроса 'ASSERT', должна быть размечена как 'noreturn' (строка N1). Это можно сделать как при помощи стандартных атрибутов (C23 и C++11):

    [[noreturn]] void assertHandler();              // since C23 or C++11

Так и специфичными для компилятора атрибутами (например, MSVC или GCC / Clang):

    __declspec(noreturn) void assertHandler();      // MSVC
    __attribute__((noreturn)) void assertHandler(); // GCC, Clang

Если нет возможности изменить исходный код и разметить функцию атрибутом, то можно сделать это c помощью системы пользовательских аннотаций в формате JSON. Для этого нужно создать файл формата JSON со следующим содержимым:

    {
      "version": 1,
      "annotations":
      [
        {
          "type": "function",
          "name": "assertHandler",
          "parameters": [],
          "attributes": [ "noreturn" ]
        },
        ....
      ]
    }

Затем этот файл должен быть включён во время анализа одним из описанных способов. В примере это происходит в строке N2.

Разметка макроса. Необходимо, чтобы анализатор учитывал, что из-за макроса 'ASSERT' выполнение кода может прерваться. Делается это при помощи комментария на строке N3. Более подробно об этом механизме можно узнать здесь.

## Задание имён функций, в которых допустим вызов макроса

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

    //-V2021_IGNORE_ASSERT_IN_FUNCTION, function: My::Qualified::Name

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

Для того, чтобы включить диагностику, можно воспользоваться механизмом включения через комментарий или директивой '#pragma pvs'.

Данная диагностика классифицируется как:

CWE-617 CERT-ERR06-C