to the top
close form
Для получения триального ключа
заполните форму ниже
Team license
Enterprise license
** Нажимая на кнопку, вы даете согласие на обработку
своих персональных данных. См. Политику конфиденциальности

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

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

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

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

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

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


Если вы так и не получили ответ, пожалуйста, проверьте папку
Spam/Junk и нажмите на письме кнопку "Не спам".
Так Вы не пропустите ответы от нашей команды.

>
>
>
PVS-Studio и враждебная среда обитания

PVS-Studio и враждебная среда обитания

28 Янв 2015

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

0303_PVS-Studio-Hostile-Environment_ru/image1.png

Я считаю PVS-Studio очень качественным продуктом. Мы можем почти в любой день сделать и выложить дистрибутив. У нас используется очень большое количество автоматизированных тестов различного уровня и типов. Вот описание некоторых из них: "Как мы тестируем анализатор кода". Сейчас их стало больше. Например, теперь для статического анализа мы используем не только свой собственный анализатор, но и Clang. Если исправленная версия прошла все тесты, значит ее можно смело выдавать пользователям.

К сожалению, вся красота и надежность внутреннего кода иногда разваливается из-за воздействий враждебной окружающей среды. В результате все впечатление от продукта портится. Вроде и не мы виноваты, но не работает-то именно наш продукт. Я могу привести большое количество примеров. Первое что вспоминается:

  • Сторонний add-in что-то портит в окружении Visual Studio. В результате приходится создавать костыль для обхода проблемы или смириться и ответить "извините, ничего не можем сделать". Один из таких примеров - "Описание ошибки интеграции Intel Parallel Studio Service Pack 1 в Visual Studio 2005/2008".
  • COM-интерфейсы Visual Studio для получения информации о проекте неожиданно могут кинуть исключение. Видимо, среда в этот неудачный момент занята чем-то еще. Вызовы приходится заворачивать в цикл для многократного их повторения. Танцы с бубном, которые не всегда помогают.
  • У разработчика стоит антивирус X и, согласно корпоративной политике, у него нет прав менять его настройки. Этот антивирус "держит" какое-то время некоторые временные файлы, и анализатор не может их удалить. В результате получается, что анализатор "гадит" в папку проекта.
  • Разное можно вспоминать. Некоторые примеры можно посмотреть здесь, здесь и здесь.

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

Один из потенциальных пользователей прислал вопрос о странном поведении PVS-Studio:

Мы сейчас гоняем триальную версию и раздумываем о покупке полной. Однако при анализе наткнулись на один нюанс, ставящий справедливость выводов под сомнение.

Ниже – скриншот, на котором видно ошибку.

filePath и cachePath отмечены как неиспользуемые (предупреждение V808), хотя видно, что они используются буквально в следующей строке после объявления.

Могли бы Вы объяснить подобное поведение анализатора?

На скриншоте можно видеть код следующего вида (код изменён):

std::string Foo()
{  
  std::string filePath(MAX_PATH + 1, 0);
  std::string cachePath = "d:\\tmp";
  if (!GetTempFileName(cachePath.c_str(), "tmp", 0,
                       &filePath.front()))
    throw MakeSystemError("...", GetLastError(), __SOURCE__);
  return std::move(filePath);
}

Ну что сказать. Позор анализатору. Ведь действительно сообщает глупость. Переменные filePath и cachePath явно используются. Для предупреждения нет никаких причин. И ладно бы функция была на 1000 строк. Нет, функция проста для безобразия.

Всё, первое впечатление испорчено. Теперь расскажу о результатах расследования.

Анализатор PVS-Studio использует для препроцессирования файлов компилятор Visual C++ (CL.exe) или Clang. Подробнее, о том, как мы используем Clang рассказано в заметке: "Немного о взаимодействии PVS-Studio и Clang".

Препроцессор компилятора Visual C++ работает качественно, но зато крайне медленно. Clang работает быстро, но зато многое не поддерживает или работает неправильно. Разработчики Clang заявляют, что очень хорошо совместимы с Visual C++, но это неправда. Есть множество мелочей, которые они не поддерживают или делают не так как Visual C++. Для анализатора эти мелочи бывают фатальны, как и произошло в этот раз.

По умолчанию анализатор PVS-Studio в начале пытается препроцессировать файл с помощью Clang. Однако он знает: Clang далеко не всегда может препроцессировать то, что может Visual C++. Если возникает ошибка препроцессирования, то запускается CL.exe. Так теряется немного времени на бесполезный запуск Clang, но в целом такой подход очень экономит время на получении *.i файлов.

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

Причиной неправильного поведения стал макрос __SOURCE__, объявленный в коде следующим образом:

#define __SLINE_0__(_line) #_line
#define __SLINE__(_line) __SLINE_0__(_line)
#define __SOURCE__ __FILE__":"__SLINE__(__LINE__)

При препроцессировании строки:

throw MakeSystemError(_T("GetTempFileName"), GetLastError(),
                      __SOURCE__);

Она должна превратиться в:

MakeSystemError("GetTempFileName", GetLastError(),
                "..path.."":""37");

Именно так и поступает компилятор Visual C++. Все отлично. Анализатор корректно обработает этот код.

Если явно задать в настройках PVS-Studio всегда использовать CL.exe, то ложные срабатывания исчезнут. Однако, если будет запущен Clang, то анализатор будет иметь дело с некорректным кодом.

Clang не может осилить макросы и на выходе мы имеем:

throw MakeSystemError("GetTempFileName", GetLastError(),
                      "..path.."":"__SLINE__(37));

Макрос __SLINE__ остался нераскрытым.

Получилась некорректная конструкция, недопустимая с точки зрения языка C++. Встретив её, анализатор PVS-Studio пытается как-то обойти некорректный код, чтобы продолжить анализ далее. Лучше что-то пропустить, чем полностью не обработать файл. Часто такие пропуски не оказывают никакого влияние на результаты анализа.

Но в этот раз обойти некорректное место безболезненно не получилось. Был выброшен весь блок:

if (!GetTempFileName(cachePath.c_str(), "tmp", 0, &filePath.front()))
  throw MakeSystemError("....", GetLastError(), __SOURCE__);
return std::move(filePath);

Так уж вышло... Анализатор сделал всё, что смог.

Раз этого фрагмента для анализатора не существует, то переменные не инициализированы, никак не используются. Это и приводит к выдаче ложного срабатывания.

Один из вариантов решения проблемы - это всегда использовать препроцессор от Visual C++. Но есть недостаток - медленный анализ.

Поэтому в данном случае мы выбрали иной путь. Так как компания, из которой к нам обратились, подумывает о приобретении PVS-Studio, мы рассмотрели этот частный случай и сделали в коде очередную подпорку. Это некрасиво, но практично. У нас уже много разных специальных мест, которые обходят какие-то нюансы, встречающиеся в проектах наших пользователей. Это является разновидностью поддержки.

Итак, в этот раз нас подвёл препроцессор, реализованный в Clang. Интересно, что станет причиной написать следующую подобную статью о внешних ошибках.

Вот так и живём. Спасибо за внимание.

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

Популярные статьи по теме
Под капотом SAST: как инструменты анализа кода ищут дефекты безопасности

Дата: 26 Янв 2023

Автор: Сергей Васильев

Сегодня речь о том, как SAST-решения ищут дефекты безопасности. Расскажу, как разные подходы к поиску потенциальных уязвимостей дополняют друг друга, зачем нужен каждый из них и как теория ложится на…
Ложные представления программистов о неопределённом поведении

Дата: 17 Янв 2023

Автор: Гость

Неопределённое поведение (UB) – непростая концепция в языках программирования и компиляторах. Я слышал много заблуждений в том, что гарантирует компилятор при наличии UB. Это печально, но неудивитель…
Топ-10 ошибок в C++ проектах за 2022 год

Дата: 29 Дек 2022

Автор: Владислав Столяров

Дело идёт к Новому году, а значит, самое время традиционно вспомнить десять самых интересных срабатываний, которые нашёл PVS-Studio в 2022 году.
PVS-Studio и RPCS3: лучшие предупреждения в один клик

Дата: 12 Дек 2022

Автор: Александр Куренев

Best Warnings — режим анализатора, оставляющий в окне вывода 10 лучших предупреждений. Мы предлагаем вам ознакомиться с обновлённым режимом Best Warnings на примере проверки проекта RPCS3.
Holy C++

Дата: 23 Ноя 2022

Автор: Гость

В этой статье постараюсь затронуть все вещи, которые можно без зазрения совести выкинуть из С++, не потеряв ничего (кроме боли), уменьшить стандарт, нагрузку на создателей компиляторов, студентов, из…

Комментарии (0)

Следующие комментарии next comments
close comment form
Unicorn with delicious cookie
Мы используем куки, чтобы пользоваться сайтом было удобно.
Хорошо