Мы используем куки, чтобы пользоваться сайтом было удобно.
Хорошо
to the top
close form

Заполните форму в два простых шага ниже:

Ваши контактные данные:

Шаг 1
Поздравляем! У вас есть промокод!

Тип желаемой лицензии:

Шаг 2
Team license
Enterprise license
** Нажимая на кнопку, вы даете согласие на обработку
своих персональных данных. См. Политику конфиденциальности
close form
Запросите информацию о ценах
Новая лицензия
Продление лицензии
--Выберите валюту--
USD
EUR
RUB
* Нажимая на кнопку, вы даете согласие на обработку
своих персональных данных. См. Политику конфиденциальности

close form
Бесплатная лицензия PVS‑Studio для специалистов Microsoft MVP
* Нажимая на кнопку, вы даете согласие на обработку
своих персональных данных. См. Политику конфиденциальности

close form
Для получения лицензии для вашего открытого
проекта заполните, пожалуйста, эту форму
* Нажимая на кнопку, вы даете согласие на обработку
своих персональных данных. См. Политику конфиденциальности

close form
Мне интересно попробовать плагин на:
* Нажимая на кнопку, вы даете согласие на обработку
своих персональных данных. См. Политику конфиденциальности

close form
check circle
Ваше сообщение отправлено.

Мы ответим вам на


Если вы так и не получили ответ, пожалуйста, проверьте, отфильтровано ли письмо в одну из следующих стандартных папок:

  • Промоакции
  • Оповещения
  • Спам

>
>
>
V645. Function call may lead to buffer …
menu mobile close menu
Проверка проектов
Сообщения PVS-Studio
Диагностики общего назначения (General Analysis, C++)
Диагностики общего назначения (General Analysis, C#)
Диагностики общего назначения (General Analysis, Java)
Микрооптимизации (C++)
Диагностика 64-битных ошибок (Viva64, C++)
Реализовано по запросам пользователей (C++)
Cтандарт MISRA
Стандарт AUTOSAR
Стандарт OWASP (C++)
Стандарт OWASP (C#)
Проблемы при работе анализатора кода
Дополнительная информация
toggle menu Оглавление

V645. Function call may lead to buffer overflow. Bounds should not contain size of a buffer, but a number of characters it can hold.

25 Июн 2021

Анализатор обнаружил потенциальную ошибку, связанную 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 будет записан уже за пределами буфера!

3) Забывают о факторе целочисленного переполнения. Рассмотрим пример такой ошибки:

struct A
{
  ....
  char consoleText[512];
};

void foo(A a)
{
  char inputBuffer[1024];
  ....
  strncat(a.consoleText, inputBuffer, 
          sizeof(a.consoleText) - strlen(a.consoleText) - 5);
}

Здесь в качестве третьего аргумента используется инфиксное выражение. При невнимательном изучении кода может показаться, что значение выражения "sizeof(a.consoleText) - strlen(a.consoleText) – 5" лежит в диапазоне [0, 507], и код корректен. Однако это не так:

  • Результат функции 'strlen(a.consoleText)' может быть в диапазоне [0, 511].

  • Если 'strlen(a.consoleText)' вернет значение от 0 до 507, то результирующее значение выражения также будет в диапазоне [0, 507], переполнения буфера 'a.consoleText' не происходит.
  • Если 'strlen(a.consoleText)' вернет значение от 508 до 511, то в результирующем выражении произойдет беззнаковое переполнение. В случае если тип 'size_t' имеет размер 64-бита, то соответственно будет получен диапазон [0xFFFFFFFFFFFFFFFC, 0xFFFFFFFFFFFFFFFF]. Получается, что в буфер как будто можно записать огромное количество символов. Естественно, это не так, и как следствие в коде происходит переполнение буфера 'a.consoleText'.

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

// 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);

// Sample N3
void foo(A a)
{
  char inputBuffer[1024];
  ....
  size_t textSize = strlen(a.consoleText);
  if (sizeof(a.consoleText) - textSize > 5u)
  {
    strncat(a.consoleText, inputBuffer, 
            sizeof(a.consoleText) - textSize - 5);
  }
  else
  {
    // ....
  }
}

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

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

Выявляемые диагностикой ошибки классифицируются согласно ГОСТ Р 71207–2024 как критические и относятся к типу: Ошибки переполнения буфера (записи или чтения за пределами выделенной для буфера памяти).

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

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