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

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

![0640_strncat_strlcat_ru/image1.png](https://import.viva64.com/docx/blog/0640_strncat_strlcat_ru/image1.png)

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

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

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

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

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

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

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

## Ошибка в Haiku OS

[V645](https://pvs-studio.ru/ru/docs/warnings/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

```cpp
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_ являются корректными:

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

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

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

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

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

## Заключение

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

1. [strncat, strncat\_s \[cppreference\.com\]](https://en.cppreference.com/w/c/string/byte/strncat);
1. [strncat \[cplusplus\.com\]](http://www.cplusplus.com/reference/cstring/strncat/);
1. [strlcat \[linux\.die\.net\]](https://linux.die.net/man/3/strlcat)\.

## P\.S\.

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