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


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

Данному виду уязвимости подвержены такие функции, как 'strncat', 'wcsncat' и так далее [1].

Описание функции 'strncat':

char *strncat(
   char *strDest,
   const char *strSource,
   size_t count 
);

Где:

  • 'destination' - строка получатель;
  • 'source' - строка источник;
  • 'count' - число добавляемых символов.

Функция 'strncat' пожалуй, одна из самых опасных строковых функций. Опасность возникает из-за того, что принцип её работы отличается от того, как представляют его себе программисты.

Третий аргумент указывает не размер буфера, а сколько ещё символов в него можно поместить. Вот цитата из описания этой функции в MSDN: "strncat does not check for sufficient space in strDest; it is therefore a potential cause of buffer overruns. Keep in mind that count limits the number of characters appended; it is not a limit on the size of strDest."

К сожалению, про это редко помнят и используют strncat неправильными способами. Можно выделить два типа ошибок:

1) Разработчики считают, что аргумент 'count' это размер буфера 'strDest'. В результате, можно видеть в программах следующий некорректный код:

char newProtoFilter[2048] = "....";
strncat(newProtoFilter, szTemp, 2048);
strncat(newProtoFilter, "|", 2048);

Программист предполагает, что передавая в качестве третьего аргумента число 2048, он защищает код от переполнения. Это не так. Он указывает, что к строке можно добавить ещё до 2048 символов!

2) Забывают, что после копирования символов, функция strncat добавит терминальный 0. Пример опасного кода:

char filename[NNN];
...
strncat(filename,
        dcc->file_info.filename,
        sizeof(filename) - strlen(filename));

На первый взгляд, кажется, что теперь программист защитился от переполнения буфера 'filename'. Но это не так. Он вычел из размера массива длину строки. Это значит, что если вся строка уже заполнена, выражение "sizeof(filename) - strlen(filename)" вернет единицу. В результате к строке будет прибавлен ещё один символ, а терминальный ноль будет записан уже за границы буфера.

Поясним эту ошибку на более простом примере:

char buf[5] = "ABCD";
strncat(buf, "E", 5 - strlen(buf));

В буфере уже нет места для новых символов. В нём находится 4 символа и терминальный ноль. Выражение "5 - strlen(buf)" равно 1. Функция strncpy() скопирует символ "E" в последний элемент массива 'buf'. Терминальный 0 будет записан уже за пределами буфера!

Чтобы исправить приведённые выше примеры, их нужно переписать следующим образом:

// Sample N1
char newProtoFilter[2048] = "....";
strncat(newProtoFilter, szTemp,
        2048 - 1 - strlen(newProtoFilter));
strncat(newProtoFilter, "|",
        2048 - 1 - strlen(newProtoFilter));

// Sample N2
char filename[NNN];
...
strncat(filename,
        dcc->file_info.filename,
        sizeof(filename) - strlen(filename) - 1);

Этот код нельзя назвать красивым или по-настоящему надежным. Гораздо лучшим решением будет отказ от функций типа 'strncat' в пользу более безопасных. Например, можно использовать класс std::string или такие функции как strncat_s и так далее [2].

Дополнительные ресурсы

Согласно Common Weakness Enumeration, потенциальные ошибки, найденные с помощью этой диагностики, классифицируются как CWE-193, CWE-787.

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


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

Проверено проектов
355
Собрано ошибок
13 303

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

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

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

goto PVS-Studio;