Что скрывают в себе комментарии




О пользе или вреде комментариев в коде программ много говорится и однозначного мнения так и не сформировалось. Однако мы решили посмотреть на них с другой стороны. Могут ли комментарии навести исследователя на скрытые ошибки в коде?

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

Мы решили, что это интересная сфера для исследований. Просматривать файлы вручную или искать обычным поиском по одному слову это очень долгий и утомительный процесс. Была написана утилита, которая ищет в ".c" и ".cpp" файлах подозрительные комментарии, основываясь на своем словаре "подозрительных слов". Например, в словарь подозрительных слов попали: fuck, bug, stupid, compiler.

Было получено очень большое количество строк с такими комментариями. Поиск фрагментов действительно стоящих внимания, стал непростым и утомительным занятием. Удалось найти мало интересного. Намного меньше, чем хотелось.

Задачей поиска было нахождение новых шаблонов возможных ошибок, которые допускают программисты. К сожалению, все места, которые были найдены или не диагностируемы статическим анализом кода или уже успешно обнаруживаются PVS-Studio.

Плохой результат, тоже результат. Скорее всего, мы сделаем вывод, что метод поиска странных комментариев тупиковый. Он слишком трудоёмок и позволяет найти совсем немного ляпов.

Но раз исследование проведено, мы решили поделиться парой примеров с вами.

Например, такой пример кода:

// Search for EOH (CRLFCRLF)
const char* pc = m_pStrBuffer;
int iMaxOff = m_iStrBuffSize - sizeof(DWORD);
for (int i = 0; i <= iMaxOff; i++) {
  if (*(DWORD*)(pc++) == 0x0A0D0A0D) {
    // VC-BUG?: '\r\n\r\n' results in 0x0A0D0A0D too,
    //although it should not!
    bFoundEOH = true;
    break;
  }
}

Как видно из комментария "// Search for EOH (CRLFCRLF)" хотели находить последовательность байт 0D,0A,0D,0A (CR == 0x0D, LF == 0x0A). Поскольку, байты располагаются в обратном порядке, константа для поиска равна 0x0A0D0A0D.

Видимо, это программа не очень успешно работает с иной последовательностью возвратов каретки и переносов строк. Это вызывает непонимание у автора, о чем говорит комментарий: " // VC-BUG?: '\r\n\r\n' results in 0x0A0D0A0D too, although it should not!". Так почему же алгоритм находит не только последовательность {0D,0A,0D,0A}, но и {0A,0D,0A,0D} ?

Всё очень просто. Алгоритм поиска движется в массиве по одному байту. Поэтому, если в программе встречается длинная последовательность вида {0A,0D,0A,0D,0A,0D,0A,...}, то алгоритм пропустит первый символ 0A и найдет дальше не то, что хотелось.

К сожалению при статическом анализе найти подобные дефекты невозможно.

Вот еще один пример странного кода :

TCHAR szCommand[_MAX_PATH * 2];
LPCTSTR lpsz = (LPCTSTR)GlobalLock(hData);
int commandLength = lstrlen(lpsz);
if (commandLength >= _countof(szCommand))
{
  // The command would be truncated.
  //This could be a security problem
  TRACE(_T("Warning: ........\n"));
  return 0;
}
// !!! MFC Bug Fix
_tcsncpy(szCommand, lpsz, _countof(szCommand) - 1);
szCommand[_countof(szCommand) - 1] = '\0';
// !!!

В данном случае "MFC Bug Fix" совершенно не соответсвует действительности, поскольку никакой ошибки в MFC здесь нет. Код не вызывает ошибок в таком виде, но возможно изначально было записано только: '_tcsncpy(szCommand, lpsz, _countof(szCommand) - 1);'. В таком случае ошибка действительно имела место быть. Но записать корректное копирование строк можно несколько короче:

_tcsncpy(szCommand, lpsz, _countof(szCommand));

Функции вида 'strncpy' сами добавляют завершающий нуль в конец строки, если строка источник не длиннее значения указанного в счетчике. А это именно так, так как выше есть соответствующая проверка. Случаи некорректного копирования строк в PVS-Studio уже успешно анализируются, поэтому ничего нового мы не узнали.

Заключение

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

  • написать простой синтаксический анализ для уменьшения нахождения "неинтересных" строк;
  • расширить словарь новыми выражениями.

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



Найдите ошибки в своем C, C++, C# и Java коде

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

goto PVS-Studio;


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

Проверено проектов
367
Собрано ошибок
13 552

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

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

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

goto PVS-Studio;