- PVSM.RU - https://www.pvsm.ru -

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

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

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

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

Недавно мы доработали ядро анализатора [1], и коллега, который просматривал новые срабатывания, обнаружил среди них ложное. Он выписал срабатывание для тимлида, тот бегло просмотрел код и создал задачу. Задачу взял я. Таким нехитрым образом и образовалась цепочка из трёх программистов.

Предупреждение анализатора: V645 [2] 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.

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

struct A
{
  char consoleText[512];
};

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

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

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

где:

  • 'destination' — строка-получатель;

  • 'source' — строка-источник;

  • 'count' — максимальное число символов, которое можно добавить.

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

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

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

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

strlen(a.consoleText) = 0

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

В статическом анализаторе за подсчёт таких выражений отвечает механизм анализа потока данных (data-flow). В большинстве случаев, когда выражение состоит из констант времени компиляции, data-flow вернёт строгое значение выражения. В остальных случаях, как и в случае с предупреждением, он сформирует только диапазон возможных значений выражения.

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

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

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

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

Получается, что анализатор прав! В данном выражении к полю consoleText может добавиться гораздо большее количество символов, чем его размер, что приведёт к переполнению буфера [3] и, как следствие, неопределённому поведению [4]. Причина неожиданного предупреждения – то, что ложного срабатывания тут нет!

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

Если хотите поделиться этой статьей с англоязычной аудиторией, то прошу использовать ссылку на перевод: Vladislav Stolyarov. One Day in the Life of PVS-Studio Developer, or How I Debugged Diagnostic That Surpassed Three Programmers [5].

Автор: Vladislav Stolyarov

Источник [6]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/c-3/365761

Ссылки в тексте:

[1] доработали ядро анализатора: https://pvs-studio.com/ru/blog/posts/cpp/0824/

[2] V645: https://pvs-studio.com/ru/w/v645/

[3] переполнению буфера: https://pvs-studio.com/ru/blog/terms/0067/

[4] неопределённому поведению: https://pvs-studio.com/ru/blog/terms/0066/

[5] One Day in the Life of PVS-Studio Developer, or How I Debugged Diagnostic That Surpassed Three Programmers: https://habr.com/en/company/pvs-studio/blog/566230/

[6] Источник: https://habr.com/ru/post/566238/?utm_source=habrahabr&utm_medium=rss&utm_campaign=566238