По мотивам «трёх интервью о статических анализаторах», или четвертое интервью

    интервью
    Примерно неделю назад на Хабре мною была опубликована статья "Три интервью о статических анализаторах кода", в которой на читательский суд были представлены точки зрения опытных программистов из Acronis, AlternativaPlatform и НПО «Эшелон» о методике разработки ПО, а также некоторые их размышления относительно использования статических анализаторов кода.

    Поскольку спонсором статьи выступила компания ООО «СиПроВер» — разработчик статического анализатора PVS-Studio, — я решил попросить Андрея Карпова (технический директор) также ответить на некоторые вопросы. А именно — прокомментировать интересные моменты всех трёх интервью. Плюс сделать обращение к коллегам и читателям. Получился вот такой интересный результат.



    Комментарий относительно примечательных моментов в интервью Акрониса


    Пару раз на конференции при неформальном общении в холле или за обеденным столом меня спрашивали: «Неужели кто-то ещё программирует на Си++?». И искренне удивлялись, когда я отвечал: «да, и это один из самых используемых языков». Просто он как-то сегодня не на слуху. Кругом php, ruby, go. Кажется, что Си++ — это «было давно и не правда». И мне приятно, что люди лишний раз увидят в статье, что, например, Acronis Backup написан на Си++ и над этим постоянно трудится 70 программистов. Сам я о будущем Си и Си++ не переживаю. Мне просто удивительно, как так выходит, что многие считают Си++ мёртвым языком.

    Приятно было также услышать, что в Acronis широко применяется практика Code Review. Часто этот метод повышения качества программ недооценивается, или считается, что он отнимает слишком много времени. Скупой платит дважды.

    Кстати, я знаю по крайней мере один пример, когда иногда умножать sizeof на sizeof всё-таки имеет практический смысл. Например, такое умножение получается, когда один sizeof() используется для взятия количества элементов в массиве. Имеется в виду вот это:
    template <typename T, size_t N>
    char (&ArraySizeHelper(T (&array)[N]))[N];
    #define arraysize(array) (sizeof(ArraySizeHelper(array)))

    Такой 'arraysize' защищает от случайной передачи в качестве аргумента не массива, а обыкновенного указателя. Подробности здесь.

    В результате, вполне может получиться конструкция вида: «sizeof(float) * (sizeof(ArraySizeHelper(array)))». Но анализатор PVS-Studio знает про такие случаи и не выдаёт предупреждений.

    Комментарий относительно примечательных моментов в интервью Альтернативы


    С Java я не знаком, поэтому не могу прокомментировать, насколько хорошо язык защищает от ошибок. Конечно, уже одно отсутствие ручного управления памятью сильно упрощает жизнь. Однако, я думаю, часть ошибок не зависит от языка. Например, если это результаты Copy-Paste. Думаю, использование статического анализатора для поиска опечаток было бы весьма уместно и для Java. Но я опять-таки не знаю, что предлагают для Java существующие анализаторы кода.

    Комментарий относительно примечательных моментов в интервью НПО Эшелон


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

    Кстати, пользуясь случаем, хотел поднять вот какую тему. Наши пользователи или потенциальные пользователи совершенно не рассматривают PVS-Studio как инструмент, способный находить уязвимости. Я этого не понимаю. Да, мы не ищем закладки в коде. Мы ориентированы на поиск ошибок, а не дефектов, которые делают ПО уязвимым. Но всё равно я не понимаю такое деление на белое и чёрное. Ведь многие ошибки можно точно так же рассматривать как уязвимость. Достаточно только посмотреть на ошибку под другим углом.

    Возьмём, например, проект UltimateTCPIP и найдем в нём с помощью PVS-Studio вот такую ошибку:
    char *CUT_CramMd5::GetClientResponse(LPCSTR ServerChallenge)
    {
      ...
      if (m_szPassword != NULL)
      {
        ...
        if (m_szPassword != '\0')
        {
      ...
    }

    V528 It is odd that pointer to 'char' type is compared with the '\0' value. Probably meant: *m_szPassword != '\0'. UTMail ut_crammd5.cpp 333

    Мы говорим о такой ошибке просто, как об опечатке. Забыли разменивать указатель. В результате не выполняется проверка, что строка пустая. Код должен был быть таким:
    if (*m_szPassword != '\0')

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

    Или ещё пример из PostgreSQL:
    char *
    px_crypt_md5(const char *pw, const char *salt,
                 char *passwd, unsigned dstlen)
    {
      ....
      unsigned char final[MD5_SIZE];
      ....
      /* Don't leave anything around in vm they could use. */
      memset(final, 0, sizeof final);
      ....
    }

    V597 The compiler could delete the 'memset' function call, which is used to flush 'final' buffer. The RtlSecureZeroMemory() function should be used to erase the private data. pgcrypto crypt-md5.c 157

    Здесь PVS-Studio обнаруживает, что массив 'final' не обнуляется перед выходом из функции. Почему так, можно узнать из описания диагностики V597.

    Вот мне и непонятно, почему диагностики PVS-Studio являются «недостаточно выявляющими уязвимости».

    Ваше видение относительно будущего статических анализаторов кода


    В целом со статическим анализом всё хорошо. Инструментарий в этом направлении быстро развивается и набирает большую популярность.

    Хотелось бы, чтобы в России это тоже происходило побыстрее. У нас практически нет рынка для инструментов статического анализа. Об этом можно судить хотя бы по количеству посещений нашего сайта, скачиванию демонстрационных версий и статистике продаж. Половину всей активности создают посетители из России. Но количество российских клиентов составляет не 50%, а только чуть-чуть. Печально.

    Обращение к читателям и коллегам по цеху


    Текст в духе «используйте в работе статический анализ» прозвучит банально. Поэтому подниму нестандартную тему.

    Желаю удачного общения между начальниками и подчинёнными. Часто указания начальства выглядят по меньшей мере странными. Но следует учитывать, что начальник часто обладает большим объёмом знаний по проекту в целом. И то, что кажется странным подчинённому, на высоком уровне может быть очень полезным или просто вынужденным. К сожалению, начальники программистов часто сами являются выходцами из программистов и потому тяготеют к интроверсии. Другими словами, ставя задачу, они не считают нужным объяснить, зачем всё это надо. Их надо понять и простить. А потом, задавая вопросы, понять, чем вызвано такое странное распоряжение. Скорее всего, начальник не против будет объяснить. Он просто забыл про это или «оптимизировал» время общения, сведя комплексную задачу к «сделай так». Соответственно, желаю и руководству не забывать объяснять свои шаги и решения.

    И спасибо организатору интервью.

    Заключение


    Итак, уважаемые читатели, можно сказать, что бонусные материалы были только что продемонстрированы Вам. Автор надеется, что Вам было интересно. На этом разрешите откланяться. Пишите качественный код и пользуйтесь самым широким спектром полезных инструментов. Всего доброго! Не прощаемся.
    PVS-Studio 547,87
    Ищем ошибки в C, C++ и C# на Windows, Linux, macOS
    Поделиться публикацией
    Комментарии 14
      +4
      Доля уязвимостей, найденных статическими анализаторами ничтожно мала по сравнению с тем, на что натыкаются эксперты при анализе кода. Стоило обратить внимание людей на шеллшок, как там нашли сразу по меньшей мере 6 (!) уязвимостей и наложили около 30 патчей.

      Из последних на моей памяти реально присвоенных CVE, что нашли анализаторы — уязвимость в Х.org, которую нашёл cppcheck. Это капля в море в пересчёте на адское количество ложных срабатываний.

      В итоге так и живём — вся суть отечественного аудита кода заключается в гуглении CVE-шек из OpenSource проектов.
        0
        Ну так статический анализатор — не большая кнопка «Сделать хорошо». Это еще один инструмент на ряду с качественной IDE и другими инструментами, помогающими в разработке.
          +1
          Так и речь не о том, что анализатор взял и выдал: «у вас CVE-2014-XXXX в строке номер 150», а о том, насколько реально помогли анализаторы при ловле этих самых CVE. Касательно аудита безопасности успешных примеров пока маловато, чтобы говорить о том, что анализатор помогает в обнаружении уязвимостей. Ибо уязвимость — это не просто ошибка, а ошибка, которую (пусть даже в теории) можно проэксплуатировать, но анализатор не может подсказать, как именно, он не знает особенности этой программы и не знает, что в этой строке может оказаться пароль или внешние данные. Именно поэтому мы редко видим новости «Анализатор ХХХ помог найти уязвимость YYY», хотя уязвимости в СПО отстреливают ежедневно.
            0
            На мой взгляд то о чем вы говорите и есть вышеупомянутая кнопка. Сейчас анализаторы находят возможные утечки, undefined behavior, опечатки — то, что может привести к падению программ, багам и возможно уязвимостям. А дальше уже обработать эту информацию должен программист.
              0
              Вот как раз вот в «обработать эту информацию должен программист собака и порылась». Статический анализатор — он работает локально и многого просто не видит. В любых программах мало-мальски разумного размера есть дикое количество кода, который либо вообще никогда не исполняется, либо никогда не вызывает ошибок определённого вида. Скажем то же самое сравнение m_szPassword != '\0' может быть типа как «настоящей» ошибкой, но если в вышестоящей функции стоит проверка if (strlen(m_szPassword) < minLen) return false; то эта ошибка никогда и никак не сможет проявиться.

              Теоретически, эту ошибку хорошо бы пофиксить всё равно, но спешки тут никакой нету. А вот если asan+fuzzer вам показали, что при определённых условиях у вас может быть выход за границы массива — то это, грубо говоря, уже пол-exploit'а. Вы уже точно знаете что ошибка проскочила через все имеющиеся у вас проверки и, почти наверняка, как-нибудь себя да проявит и «в бою».
        +3
        Acronis это вы метко привели. Мы уже два года почти используем их кривущий в доску бэкап в Parallels Cloud Storage, мало того, что скорость его зависит от фазы луны, так еще без использования их тулкитов его невозможно распаковать ни клиенту, ни владельцу продукта — в доску проприетарный формат.

        Так что плохой пример и про Акронис ничего хорошего сказать не хочется.
          +2
          Acronis Backup написан на Си++ и над этим постоянно трудится 70 программистов.

          А писался бы на Java/C#/чем-нибудь подобном, возможно, вместо 70 программистов хватило бы 30. Цифрами гордиться нужно аккуратно, с оглядкой… :)
            –2
            Я уверен, что Вы переоцениваете вклад языка в разработку больших проектов. В таких проектах на передний план выступают совсем другие нюансы, сложности, организационные задачи и так далее, а вовсе не тип используемого языка. Разница составит максимум проценты (и ещё не известно, в какую сторону).
              0
              У вас в каждом посте не менее трети (а то и половины) ошибок — это специфика именно низкоуровневых языков, вроде ручного управление памятью. По-моему, снижение числа багов из-за используемого языка на 30-50% — достаточно полезная штука.

              Обращаю внимание, я не отрицаю важности низкоуровневых языков — для встраиваемого железа или мест, где нужно вытянуть предельную производительность (те же драйвера, игровые движки, системные утилиты) без них никак. Но там, где производительность не является ключевым приоритетом — там, по-моему, managed-языки предпочтительны.
                +2
                (Снижение числа багов на 30-50%) != (Ускорение разработки проекта на 30-50%)
                  0
                  для встраиваемого железа или мест, где нужно вытянуть предельную производительность

                  Еще про кроссплатформенность забыли
              0
              Здравствуйте!

              Мы пользуемся cmake под Линукс. Насколько я понял, PVS-Studio существует только под Windows. Мне бы хотелось проверить наш проект статистическим анализатором кода, и, при наличии серьёзных результатов, выдвинуть предложение руководству по внедрению.

              Какие есть решения под Линукс и cmake?
                0
                Если есть возможность дать нам исходники, мы можем попробовать проверить проект и сделать презентацию по результатам. Если нет, то Вы можете попробовать подготовить препроцессированные *.i файлы и проверить их с помощью Standalone версии PVS-Studio. Результат может быть не очень, но для попробовать, что-то посмотреть и найти — подойдёт. Потом уже можно рассмотреть приобретение лицензии и доработку напильником для нужд проекта.
                  0
                  Coverity, например

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

                Самое читаемое