# V773. Function exited without releasing the pointer/handle. A memory/resource leak is possible.

Анализатор обнаружил потенциально возможную утечку памяти в коде. Такая ситуация возникает, когда память, выделенная с помощью 'malloc' или 'new', не была далее освобождена.

Рассмотрим пример такого кода:

    int *NewInt()
    {
      int *p = new (std::nothrow) int;
      ....
      return p;
    }

    bool Test()
    {
      int *p = NewInt();
      int res = *p;
      return res;
    }

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

Исправленный код, где не возникает утечка памяти:

    int *NewInt()
    {
      int *p = new int;
      ....
      return p;
    }

    bool Test()
    {
      int *p = NewInt();
      int res = *p;
      delete p;
      return res;
    }

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

    bool Test()
    {
      int *p = (int*)malloc(sizeof(int));
      int *q = (int*)malloc(sizeof(int));
      if (p == nullptr || q == nullptr)
      {
        std::cerr << "No memory";
        return -1;
      }
      int res = *p + *q;
      free(p);
      free(q);
      return res;
    }

Может сложиться ситуация, когда указатель 'p' будет хранить указатель на выделенную память, а 'q' будет равен 'nullptr'. Тогда выделенная память освобождена не будет. Кстати, может быть и наоборот. В параллельной программе реальна ситуация, когда первый раз не удастся выделить память, а потом удастся.

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

    void LoadBuffer(char *buf, size_t len)
    {
      FILE* f = fopen("my_file.bin", "rb");
      fread(buf, sizeof(char), len, f);
    }

Примечание. В современном C++ лучше обходиться без ручного управления ресурсами и использовать умные указатели. Например, можно рекомендовать использовать 'std::unique_ptr'. В этом случае вся память будет освобождена корректно во всех точках выхода функции. Также такое решение будет exception-safe.

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

    //+V773:SUPPRESS, class:className, namespace:nsName

Параметр 'namespace' является необязательным.

Рассмотрим пример:

    void foo()
    {
      EVENT* event = new EVENT;
      event->send();
    }

Объект класса 'EVENT' не должен удаляться в этой функции, поэтому отключить все предупреждения V773 на этот класс можно с помощью комментария:

    //+V773:SUPPRESS, class:EVENT

Выявляемые диагностикой ошибки классифицируются согласно ГОСТ Р 71207–2024 как критические и относятся к типу: Ошибки утечек памяти, незакрытых файловых дескрипторов и дескрипторов сетевых соединений [* см. примечание касательно языков C#, Java].

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

CWE-401 CWE-775 CERT-MEM31-C CERT-MEM51-CPP

Взгляните на примеры ошибок, обнаруженных с помощью диагностики V773.