﻿# Один день из жизни разработчика PVS\-Studio, или отладка диагностики, которая оказалась внимательнее трёх программистов

Главное предназначение статических анализаторов – найти те ошибки, которые остались незамеченными разработчиком\. И недавно команда PVS\-Studio снова столкнулась с интересным примером мощи этой методики\.

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

Работа с инструментами статического анализа кода требует внимательности\. Часто код, на который указал анализатор, кажется корректным\. В таких случаях хочется посчитать предупреждение ложным срабатыванием\. На днях мы сами попали в такую ловушку\. Вот как это получилось\.

Недавно мы [доработали ядро анализатора](https://pvs-studio.ru/ru/blog/posts/cpp/0824/), и коллега, который просматривал новые срабатывания, обнаружил среди них ложное\. Он выписал срабатывание для тимлида, тот бегло просмотрел код и создал задачу\. Задачу взял я\. Таким нехитрым образом и образовалась цепочка из трёх программистов\.

Предупреждение анализатора: [V645](https://pvs-studio.ru/ru/docs/warnings/v645/) The 'strncat' function call could lead to the 'а\.consoleText' buffer overflow\. The bounds should not contain the size of the buffer, but a number of characters it can hold\.

Фрагмент кода:

```cpp
struct A
{
  char consoleText[512];
};

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

Перед изучением примера давайте вспомним, что делает функция _strncat_:

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

где:

* 'destination' — строка\-получатель;
* 'source' — строка\-источник;
* 'count' — максимальное число символов, которое можно добавить\.

На первый взгляд, кажется, что с кодом всё отлично\. В нём вычисляется количество пустого места в буфере\. При этом там даже запас в 4 байта вроде как есть\.\.\. И именно из\-за этого ощущения, что с кодом всё хорошо, он и был выписан как пример, где анализатор выдаёт ложное предупреждение\.

Давайте разберёмся, как обстоят дела на самом деле\. В выражении:

```cpp
sizeof(a.consoleText) – strlen(a.consoleText) – 5
```

максимальное значение может быть достигнуто при минимальном значении второго операнда:

```cpp
strlen(a.consoleText) = 0
```

Тогда результатом будет 507, и никакого переполнения не случится\. О чём же тогда говорит PVS\-Studio? Давайте немного углубимся во внутренние механизмы анализатора и попробуем разобраться\.

В статическом анализаторе за подсчёт таких выражений отвечает механизм [анализа потока данных](https://pvs-studio.ru/ru/blog/terms/7004/) \(data\-flow\)\. В большинстве случаев, когда выражение состоит из констант времени компиляции, data\-flow вернёт строгое значение выражения\. В остальных случаях, как и в случае с предупреждением, он сформирует только диапазон возможных значений выражения\.

В данном случае значение операнда _strlen\(a\.consoleText\)_ неизвестно нам на этапе компиляции\. Давайте посмотрим диапазон\.

Спустя несколько минут отладки мы получаем от data\-flow аж 2 диапазона:

```cpp
[0, 507] U [0xFFFFFFFFFFFFFFFC, 0xFFFFFFFFFFFFFFFF]
```

На первый взгляд, кажется, что второй диапазон лишний\. Однако это не так\. Просто мы забыли о том, что результатом выражения может стать отрицательное число\. Например, такое может случиться при _strlen\(a\.consoleText\) \= 508_\. В таком случае произойдет переполнение беззнакового числа, и результатом выражения будет максимальное значение результирующего типа, в данном случае — _size\_t_\.

Получается, что анализатор прав\! В данном выражении к полю _consoleText_ может добавиться гораздо большее количество символов, чем его размер, что приведёт к [переполнению буфера](https://pvs-studio.ru/ru/blog/terms/0067/) и, как следствие, [неопределённому поведению](https://pvs-studio.ru/ru/blog/terms/0066/)\. Причина неожиданного предупреждения – то, что ложного срабатывания тут нет\!

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