Про разницу между strlcat и strncat




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

Picture 1

Описание функций

Функция strncat служит для конкатенации строк и имеет следующую сигнатуру:

char *strncat(char *dest, const char *src, size_t n);

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

Функция strlcat служит для конкатенации строк и делает это более безопасно, чем функция strncat. strlcat имеет следующую сигнатуру:

size_t strlcat(char *dst, const char *src, size_t size)

В отличие от других функций, она принимает полный размер буфера и гарантирует наличие терминального символа у результата. Для корректной работы функции strlcat, ей необходимо передавать только нультерминальные строки.

Ошибка в Haiku OS

V645 The 'strncat' function call could lead to the 'output' buffer overflow. The bounds should not contain the size of the buffer, but a number of characters it can hold. NamespaceDump.cpp 101

static void
dump_acpi_namespace(acpi_ns_device_info *device, char *root, int indenting)
{
  char result[255];
  char output[320];
  char tabs[255] = "";
  char hid[16] = "";
  int i;
  size_t written = 0;
  for (i = 0; i < indenting; i++)
    strlcat(tabs, "|    ", sizeof(tabs));

  strlcat(tabs, "|--- ", sizeof(tabs));
  ....
  void *counter = NULL;
  while (....) {
    uint32 type = device->acpi->get_object_type(result);
    snprintf(output, sizeof(output), "%s%s", tabs, result + depth);
    switch(type) {
      case ACPI_TYPE_INTEGER:
        strncat(output, "     INTEGER", sizeof(output));
        break;
      case ACPI_TYPE_STRING:
        strncat(output, "     STRING", sizeof(output));
        break;
      case ACPI_TYPE_BUFFER:
        strncat(output, "     BUFFER", sizeof(output));
        break;
      case ACPI_TYPE_PACKAGE:
        strncat(output, "     PACKAGE", sizeof(output));
        break;
      ....
      case ACPI_TYPE_MUTEX:
        strncat(output, "     MUTEX", sizeof(output));
        break;
      case ACPI_TYPE_REGION:
        strncat(output, "     REGION", sizeof(output));
        break;
      case ACPI_TYPE_POWER:
        strncat(output, "     POWER", sizeof(output));
        break;
      case ACPI_TYPE_PROCESSOR:
        strncat(output, "     PROCESSOR", sizeof(output));
        break;
      case ACPI_TYPE_THERMAL:
        strncat(output, "     THERMAL", sizeof(output));
        break;
      case ACPI_TYPE_BUFFER_FIELD:
        strncat(output, "     BUFFER_FIELD", sizeof(output));
        break;
      case ACPI_TYPE_ANY:
      default:
        break;
    }
    ....
  }
  ....
}

Анализатор обнаружил смешанный код, состоящий из вызовов функций strlcat и strncat. Причём, вызовы функции strlcat являются корректными:

char tabs[255] = "";
....
strlcat(tabs, "|--- ", sizeof(tabs));

в них передаётся нультерминальная строка и размер всего буфера.

В то же время, многочисленные вызовы strncat в цикле являются ошибочными и могут привести к ошибке:

char output[320];
....
strncat(output, "     INTEGER", sizeof(output));

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

Заключение

Полезные ссылки по теме:

P.S.

Не дожидаясь публикации основных больших статей , мы отправили отчёт разработчикам Haiku OS, и они уже приступили к исправлению ошибок: https://git.haiku-os.org/haiku/log/?qt=grep&q=pvs



Найдите ошибки в своем C, C++, C# и Java коде

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

goto PVS-Studio;


Найденные ошибки

Проверено проектов
346
Собрано ошибок
13 188

А ты совершаешь ошибки в коде?

Проверь с помощью
PVS-Studio

Статический анализ
кода для C, C++, C#
и Java

goto PVS-Studio;