# V505. The 'alloca' function is used inside the loop. This can quickly overflow stack.

Анализатор обнаружил использование функции 'alloca' внутри цикла.

Функция 'alloca' выделяет память для заданного буфера внутри фрейма вызывающей функции. Эта область памяти будет очищена только вместе с уничтожением этого фрейма в момент её завершения.

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

    void foo ()
    {
      char *buffer = nullptr;
      buffer = (char *) alloca(256); // <= (1)

      // using buffer
      ....
    } // <= (2)

Пример синтетический, но на нём можно увидеть принцип работы функция 'alloca'. На строчке, помеченной как (1), происходит выделение блока памяти размеров в 256 байт на фрейме стека функции 'foo', который будет создан при её вызове. Фрейм функции будет уничтожен на строчке (2), когда она вернёт поток управления вызывающему коду. Это освободит всю выделенную под него память на стеке и позволит избежать утечек памяти.

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

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

    void bar(int n)
    {
      for (size_t i = 0; i < n; ++i)
      {
        char *buffer = nullptr;
        if (buffer = (char*) alloca(256)) // <=
        {
          // using buffer
          ....
        }
      }
    }

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

Исправить некорректный код в данном случае достаточно просто. Для этого можно перенести вызов функции 'alloca' за пределы цикла и использовать 'buffer' повторно на каждой итерации:

    void bar(int n)
    {
      char *buffer = (char*)alloca(256);

      for (size_t i = 0; i < n; ++i)
      {
        // using buffer
        ....
      }
    }

Ещё один пример опасного кода:

    // A2W defined in ATL using alloca
    #define A2W(lpa) ....

    void AtlExample()
    {
      ....

      size_t n = ....;
      wchar_t** strings = { '\0' };
      LPCSTR* pszSrc = { '\0' };

      for (size_t i = 0; i < n; ++i)
      {
        if (wcscmp(strings[i], A2W(pszSrc[i])) == 0) // <=
        {
          ....
        }
      }
    }

Макрос 'A2W' определён в библиотеке 'ATL' версии 3.0. Внутри него используется функция 'alloca'. Приведёт ли данный код к ошибке или нет, будет зависеть от длины обрабатываемых строк, их количества и размера доступного стека. Исправить опасный код можно использовав класс 'CA2W', определённый в библиотеке 'ATL' версии 7.0. В отличии от макроса, он выделяет память на стеке только для маленьких строк. Для длинных она выделяется через 'malloc'. Кроме того, память, выделенная на стеке, будет освобождена при выходе из области видимости объявления переменной. Следовательно, будет освобождаться после сравнения с 'strings[i]'.

Исправленный пример:

    // using ATL 7.0
    ....
    for (size_t i = 0; i < n; ++i)
    {
      if (wcscmp(strings[i], CA2W(pszSrc[i])) == 0) // <=
      {
        ....
      }
    }

Подробнее про функции библиотеки ATL можно почитать в документации.

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

CWE-770 CERT-MEM05-C

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