Это моя очередная заметка о том, как PVS-Studio делает программы более надёжными. То есть где, и какие ошибки он обнаруживает. На этот раз под молоток попали примеры, демонстрирующие работу с библиотекой IPP 7.0 (Intel Performance Primitives Library).
Полухин Антон, Как делать не надо: C++ велосипедостроение для профессионаловSergey Platonov
В докладе перед нами откроется великолепный мир велосипедов и устаревших технологий, которые люди продолжают переносить в новые проекты и повсеместно использовать. Мы поговорим о:
Copy-On-Write
разработке без оглядки на готовые решения и к чему это приводит
force inline
оптимизациях, которые отлично себя показывают на бенчмарках и плохо себя ведут в реальной жизни
бездумно отключаемых оптимизациях компилятора
тонкостях стандартной библиотеки для повседневного использования
супер качественном велосипедостроении
Использование юнит-тестов для повышения качества разработкиvictor-yastrebov
В докладе рассмотрены подходы к созданию надежных юнит-тестов, которые просты в поддержке и модернизации, а также принципы создания кода пригодного для покрытия автотестами. Приведены два способа внедрения зависимости: с использованием конструктора тестируемого объекта, а также с использованием подхода "выделить и переопределить". Каждый из способов разобран на примере, демонстрирующем особенности его реализации и применения. Приведен ряд практических советов, нацеленных на создание надежных юнит-тестов. Использование на практике приведенных подходов и принципов позволяет упростить процесс поддержки и модификации существующего кода, а также дает уверенность в надежности работы добавляемого нового функционала. В конечном итоге это приводит к повышению качества разрабатываемого продукта.
Евгений Рыжков, Андрей Карпов Как потратить 10 лет на разработку анализатора ...Platonov Sergey
Кто-то верно подметил, что разработчики статических анализатора часто сталкиваются с "проблемой айсберга". Им сложно объяснить разработчикам, почему сложно написать и развивать статические анализаторы кода. Дело в том, что сторонние наблюдатели видят только вершину всего процесса, так как им доступен для изучения только простой интерфейс, который предоставляют анализаторы для взаимодействия с миром. Это ведь не графический редактор с сотнями кнопок и рычажков. В результате и возникает ощущение, что раз прост интерфейс взаимодействия, то и прост продукт. На самом деле статические анализаторы кода — это сложные программы, в которых живут и взаимодействуют разнообразнейшие методы поиска дефектов. В них реализуется множество экспертные системы, выдающие заключения о коде на основе как точных, так и эмпирических алгоритмах. В парном докладе, основатели анализатора PVS-Studio расскажут о том, как незаметно потратить 10 лет, чтобы написать хороший анализатор. Дьявол кроется в деталях!
Дмитрий Кашицын, Троллейбус из буханки: алиасинг и векторизация в LLVMSergey Platonov
Зачастую, знакомство с алиасингом в C++ у многих программистов начинается и заканчивается одинаково: -fno-strict-aliasing. На вопросы новичка, более опытные коллеги отвечают в стиле: «не трогай! а то все сломаешь!». Новичок и не трогает. В докладе будет предпринята попытка заглянуть под капот и понять, что же там, внутри. Что такое алиасинг, где он может быть полезен и какие реальные преимущества дает. Тема будет рассмотрена и со стороны программиста и со стороны разработчика компилятора. А по сему, вопрос «зачем?» будет центральным в повествовании.
Сергей Шамбир, Адаптация Promise/A+ для взаимодействия между C++ и JavascriptSergey Platonov
Шаблоны — мощный инструмент, добавляющий в язык новые возможности, а программистам в команде — новые проблемы. Доклад покажет, как тщательно продуманный шаблонный код может не усложнить, а упростить жизнь и дать надёжную абстракцию межпроцессных межъязыковых асинхронных вызовов функций. С помощью шаблонов можно:
адаптировать Promise/A+ из Javascript для C++
автоматически проверять и раскладывать динамический массив аргументов на статичные аргументы функции
сделать аналог std::bind для weak_ptr.
Эти вещи будут показаны на примере взаимных вызовов между C++ и Javascript в одном приложении с помощью CEF3.
Полухин Антон, Как делать не надо: C++ велосипедостроение для профессионаловSergey Platonov
В докладе перед нами откроется великолепный мир велосипедов и устаревших технологий, которые люди продолжают переносить в новые проекты и повсеместно использовать. Мы поговорим о:
Copy-On-Write
разработке без оглядки на готовые решения и к чему это приводит
force inline
оптимизациях, которые отлично себя показывают на бенчмарках и плохо себя ведут в реальной жизни
бездумно отключаемых оптимизациях компилятора
тонкостях стандартной библиотеки для повседневного использования
супер качественном велосипедостроении
Использование юнит-тестов для повышения качества разработкиvictor-yastrebov
В докладе рассмотрены подходы к созданию надежных юнит-тестов, которые просты в поддержке и модернизации, а также принципы создания кода пригодного для покрытия автотестами. Приведены два способа внедрения зависимости: с использованием конструктора тестируемого объекта, а также с использованием подхода "выделить и переопределить". Каждый из способов разобран на примере, демонстрирующем особенности его реализации и применения. Приведен ряд практических советов, нацеленных на создание надежных юнит-тестов. Использование на практике приведенных подходов и принципов позволяет упростить процесс поддержки и модификации существующего кода, а также дает уверенность в надежности работы добавляемого нового функционала. В конечном итоге это приводит к повышению качества разрабатываемого продукта.
Евгений Рыжков, Андрей Карпов Как потратить 10 лет на разработку анализатора ...Platonov Sergey
Кто-то верно подметил, что разработчики статических анализатора часто сталкиваются с "проблемой айсберга". Им сложно объяснить разработчикам, почему сложно написать и развивать статические анализаторы кода. Дело в том, что сторонние наблюдатели видят только вершину всего процесса, так как им доступен для изучения только простой интерфейс, который предоставляют анализаторы для взаимодействия с миром. Это ведь не графический редактор с сотнями кнопок и рычажков. В результате и возникает ощущение, что раз прост интерфейс взаимодействия, то и прост продукт. На самом деле статические анализаторы кода — это сложные программы, в которых живут и взаимодействуют разнообразнейшие методы поиска дефектов. В них реализуется множество экспертные системы, выдающие заключения о коде на основе как точных, так и эмпирических алгоритмах. В парном докладе, основатели анализатора PVS-Studio расскажут о том, как незаметно потратить 10 лет, чтобы написать хороший анализатор. Дьявол кроется в деталях!
Дмитрий Кашицын, Троллейбус из буханки: алиасинг и векторизация в LLVMSergey Platonov
Зачастую, знакомство с алиасингом в C++ у многих программистов начинается и заканчивается одинаково: -fno-strict-aliasing. На вопросы новичка, более опытные коллеги отвечают в стиле: «не трогай! а то все сломаешь!». Новичок и не трогает. В докладе будет предпринята попытка заглянуть под капот и понять, что же там, внутри. Что такое алиасинг, где он может быть полезен и какие реальные преимущества дает. Тема будет рассмотрена и со стороны программиста и со стороны разработчика компилятора. А по сему, вопрос «зачем?» будет центральным в повествовании.
Сергей Шамбир, Адаптация Promise/A+ для взаимодействия между C++ и JavascriptSergey Platonov
Шаблоны — мощный инструмент, добавляющий в язык новые возможности, а программистам в команде — новые проблемы. Доклад покажет, как тщательно продуманный шаблонный код может не усложнить, а упростить жизнь и дать надёжную абстракцию межпроцессных межъязыковых асинхронных вызовов функций. С помощью шаблонов можно:
адаптировать Promise/A+ из Javascript для C++
автоматически проверять и раскладывать динамический массив аргументов на статичные аргументы функции
сделать аналог std::bind для weak_ptr.
Эти вещи будут показаны на примере взаимных вызовов между C++ и Javascript в одном приложении с помощью CEF3.
Некоторые паттерны реализации полиморфного поведения в C++ – Дмитрий Леванов,...Yandex
Основываясь на опыте разработки Крипты, Дмитрий рассмотрит средства реализации статического и динамического полиморфизма в C++, а также некоторые их паттерны и антипаттерны.
Использование шаблонов и RTTI для конфигурации симулятора флеш-накопителя - Г...Yandex
Флеш-накопители используются в самых разных устройствах, от мобильных телефонов до компьютеров и серверов. Для каждой модели накопителя нужна прошивка с определённым набором параметров, которые могут отличаться в зависимости от ситуации. В докладе будет описан универсальный фреймфорк на С++, который предоставляет разработчикам симуляторов простой, прозрачный и быстрый доступ к любому параметру. Тестировщикам же он позволяет управлять конфигурациями при помощи стандартных инструментов редактирования и слияния.
Юрий Ефимочев, Компилируемые в реальном времени DSL для С++ Sergey Platonov
В последнее время в промышленной разработке ПО особую популярность обретают Domain-Specific Lanugages (DSL). Они драматически упрощают разработку и дают возможность “программировать” не только программистам, но и пользователям прикладных программ.
В своем докладе я расскажу об опыте использования DSL применительно к С++, причем упор будет сделан на производительность кода DSL, и его мгновенную “встраиваемость” в запущенную программу путем компиляции DSL-кода в нативный код с помощью инструментария LLVM.
Модель памяти C++ - Андрей Янковский, ЯндексYandex
В докладе Андрей расскажет о моделях памяти различных процессоров, о тонкостях реализации неблокирующих алгоритмов и о том, какое отношение всё это имеет к С++.
В рамках данного выступления вас ждут:
* рассказ о полезных и интересных вещах из Boost
* новости с передовиц разработки Boost и о новинках ожидаемых в следующих версиях
* что из Boost готовится к переезду в новый стандарт С++
* как экспериментировать с Boost, имея под рукой только браузер
* что людям не нравится в Boost и как с этими людьми бороться (-:
Евгений Зуев, С++ в России: Стандарт языка и его реализацияPlatonov Sergey
Доклад посвящён различным аспектам компилятора С++, созданного с участием автора. В выступлении рассказывается о продвинутой архитектуре компилятора, основных проектных решениях, а также обсуждаются особенности входного языка, повлиявшие на реализацию компилятора.
Современный статический анализ кода: что умеет он, чего не умели линтерыcorehard_by
Статический анализ появился почти 40 лет назад. В своём докладе мы хотим показать, чему за это время научились статические анализаторы. Мы рассмотрим различные методики анализа, как они появлялись и какие ошибки можно найти с помощью них. Посмотрим на примеры ошибок, найденных PVS-Studio в Open Source проектах. Поговорим о том, чем статический анализатор отличается от "линтеров" и некоторых других инструментов, а также какие проблемы решает современный статический анализатор C++ кода, помимо собственно анализа кода.
Павел Беликов
@PVS-Studio, Тула, Россия
ПВТ - весна 2015 - Лекция 7. Модель памяти С++. Внеочередное выполнение инстр...Alexey Paznikov
ЛЕКЦИЯ 7. Модель памяти С++. Атомарные операции. Внеочередное выполнение инструкций. Барьеры памяти. Семантика захвата-освобождения
Курс "Параллельные вычислительные технологии" (ПВТ), весна 2015
Сибирский государственный университет телекоммуникаций и информатики
Пазников Алексей Александрович
к.т.н., доцент кафедры вычислительных систем СибГУТИ
http://cpct.sibsutis.ru/~apaznikov
Слайды доклада на конференции C++ Corehard Winter 2017 (г.Минск).
Автор доклада давно и успешно использует Модель Акторов при разработке приложений на C++. В основном это был положительный опыт. Но есть некоторые неочевидные моменты, про которые было бы хорошо узнать заранее. О том, где использование Модели Акторов уместно, а где нет, на какие грабли довелось наступить, какие шишки были набиты, как можно упростить себе жизнь и пойдет речь в докладе.
Разница в подходах анализа кода компилятором и выделенным инструментомTatyanazaxarova
У компилятора и сторонних инструментов статического анализа кода есть общая задача - выявление опасных фрагментов кода. Однако существует существенная разница в том, анализ какого типа они осуществляют. Я попробую на примере компилятора Intel C++ и анализатора PVS-Studio продемонстрировать различия подходов, и пояснить, чем они вызваны.
Объектно-Ориентированное Программирование на C++, Лекции 3 и 4 Dima Dzuba
Описываются возможности C++ по работе с наследованием (virtual, override, final). Описываются механизмы работы с константными переменными и методами (const, mutable, constexpr). Описываются возможности по перегрузке операторов (operator).
Григорий Демченко, Асинхронность и неблокирующая синхронизацияSergey Platonov
Практика показывает, что использование подхода, основанного на колбеках для асинхронного программирования обычно не является удобным и подвержено различным ошибкам. Для упрощения написания и поддержки сложных асинхронных программ можно использовать несколько иной подход: использовать сопрограммы для переключения контекста на время ожидания события. Такой подход позволяет реализовать интересные неблокирующие примитивы, включая неблокирующее сетевое взаимодействие, неблокирующие мьютексы, а также удобное переключение между различными пулами потоков для разнесения выполнения задач, которые требуют различные ресурсы.
The document discusses the history and properties of diamonds. It notes that diamonds were first discovered in India around 800 BC and were believed to have mystical powers. The document outlines the four characteristics used to determine the price of diamonds - carat, color, clarity, and cut. It also lists the ten richest diamond mines in the world, including mines in South Africa, Botswana, Canada, Australia, and Angola.
The document discusses the benefits of exercise for mental health. Regular physical activity can help reduce anxiety and depression and improve mood and cognitive functioning. Exercise causes chemical changes in the brain that may help protect against mental illness and improve symptoms.
Некоторые паттерны реализации полиморфного поведения в C++ – Дмитрий Леванов,...Yandex
Основываясь на опыте разработки Крипты, Дмитрий рассмотрит средства реализации статического и динамического полиморфизма в C++, а также некоторые их паттерны и антипаттерны.
Использование шаблонов и RTTI для конфигурации симулятора флеш-накопителя - Г...Yandex
Флеш-накопители используются в самых разных устройствах, от мобильных телефонов до компьютеров и серверов. Для каждой модели накопителя нужна прошивка с определённым набором параметров, которые могут отличаться в зависимости от ситуации. В докладе будет описан универсальный фреймфорк на С++, который предоставляет разработчикам симуляторов простой, прозрачный и быстрый доступ к любому параметру. Тестировщикам же он позволяет управлять конфигурациями при помощи стандартных инструментов редактирования и слияния.
Юрий Ефимочев, Компилируемые в реальном времени DSL для С++ Sergey Platonov
В последнее время в промышленной разработке ПО особую популярность обретают Domain-Specific Lanugages (DSL). Они драматически упрощают разработку и дают возможность “программировать” не только программистам, но и пользователям прикладных программ.
В своем докладе я расскажу об опыте использования DSL применительно к С++, причем упор будет сделан на производительность кода DSL, и его мгновенную “встраиваемость” в запущенную программу путем компиляции DSL-кода в нативный код с помощью инструментария LLVM.
Модель памяти C++ - Андрей Янковский, ЯндексYandex
В докладе Андрей расскажет о моделях памяти различных процессоров, о тонкостях реализации неблокирующих алгоритмов и о том, какое отношение всё это имеет к С++.
В рамках данного выступления вас ждут:
* рассказ о полезных и интересных вещах из Boost
* новости с передовиц разработки Boost и о новинках ожидаемых в следующих версиях
* что из Boost готовится к переезду в новый стандарт С++
* как экспериментировать с Boost, имея под рукой только браузер
* что людям не нравится в Boost и как с этими людьми бороться (-:
Евгений Зуев, С++ в России: Стандарт языка и его реализацияPlatonov Sergey
Доклад посвящён различным аспектам компилятора С++, созданного с участием автора. В выступлении рассказывается о продвинутой архитектуре компилятора, основных проектных решениях, а также обсуждаются особенности входного языка, повлиявшие на реализацию компилятора.
Современный статический анализ кода: что умеет он, чего не умели линтерыcorehard_by
Статический анализ появился почти 40 лет назад. В своём докладе мы хотим показать, чему за это время научились статические анализаторы. Мы рассмотрим различные методики анализа, как они появлялись и какие ошибки можно найти с помощью них. Посмотрим на примеры ошибок, найденных PVS-Studio в Open Source проектах. Поговорим о том, чем статический анализатор отличается от "линтеров" и некоторых других инструментов, а также какие проблемы решает современный статический анализатор C++ кода, помимо собственно анализа кода.
Павел Беликов
@PVS-Studio, Тула, Россия
ПВТ - весна 2015 - Лекция 7. Модель памяти С++. Внеочередное выполнение инстр...Alexey Paznikov
ЛЕКЦИЯ 7. Модель памяти С++. Атомарные операции. Внеочередное выполнение инструкций. Барьеры памяти. Семантика захвата-освобождения
Курс "Параллельные вычислительные технологии" (ПВТ), весна 2015
Сибирский государственный университет телекоммуникаций и информатики
Пазников Алексей Александрович
к.т.н., доцент кафедры вычислительных систем СибГУТИ
http://cpct.sibsutis.ru/~apaznikov
Слайды доклада на конференции C++ Corehard Winter 2017 (г.Минск).
Автор доклада давно и успешно использует Модель Акторов при разработке приложений на C++. В основном это был положительный опыт. Но есть некоторые неочевидные моменты, про которые было бы хорошо узнать заранее. О том, где использование Модели Акторов уместно, а где нет, на какие грабли довелось наступить, какие шишки были набиты, как можно упростить себе жизнь и пойдет речь в докладе.
Разница в подходах анализа кода компилятором и выделенным инструментомTatyanazaxarova
У компилятора и сторонних инструментов статического анализа кода есть общая задача - выявление опасных фрагментов кода. Однако существует существенная разница в том, анализ какого типа они осуществляют. Я попробую на примере компилятора Intel C++ и анализатора PVS-Studio продемонстрировать различия подходов, и пояснить, чем они вызваны.
Объектно-Ориентированное Программирование на C++, Лекции 3 и 4 Dima Dzuba
Описываются возможности C++ по работе с наследованием (virtual, override, final). Описываются механизмы работы с константными переменными и методами (const, mutable, constexpr). Описываются возможности по перегрузке операторов (operator).
Григорий Демченко, Асинхронность и неблокирующая синхронизацияSergey Platonov
Практика показывает, что использование подхода, основанного на колбеках для асинхронного программирования обычно не является удобным и подвержено различным ошибкам. Для упрощения написания и поддержки сложных асинхронных программ можно использовать несколько иной подход: использовать сопрограммы для переключения контекста на время ожидания события. Такой подход позволяет реализовать интересные неблокирующие примитивы, включая неблокирующее сетевое взаимодействие, неблокирующие мьютексы, а также удобное переключение между различными пулами потоков для разнесения выполнения задач, которые требуют различные ресурсы.
The document discusses the history and properties of diamonds. It notes that diamonds were first discovered in India around 800 BC and were believed to have mystical powers. The document outlines the four characteristics used to determine the price of diamonds - carat, color, clarity, and cut. It also lists the ten richest diamond mines in the world, including mines in South Africa, Botswana, Canada, Australia, and Angola.
The document discusses the benefits of exercise for mental health. Regular physical activity can help reduce anxiety and depression and improve mood and cognitive functioning. Exercise causes chemical changes in the brain that may help protect against mental illness and improve symptoms.
El documento habla sobre el poder de las redes y cómo han evolucionado a través de la historia. Explica conceptos como ciberactivismo y blogsfera, describiendo cómo las redes sociales distribuidas han empoderado a las personas al permitirles difundir información y opiniones de manera pública. También discute cómo las redes han eliminado la separación entre emisores y receptores de información, creando una esfera pública democrática y universal.
Сравнение статического анализа общего назначения из Visual Studio 2010 и PVS-...Tatyanazaxarova
В статье показаны ошибки, выявленные с помощью статического анализатора кода, встроенного в Visual Studio 2010. Исследование проводилось на пяти open source проектах. Эти же проекты были проверены с помощью PVS-Studio. Приведены результаты сравнения этих двух инструментов.
Выполняет анализ кода на языках: C, C++, C++/CLI, C++/CX, C#. Plugin для Visual Studio 2010-2015. Интеграция с SonarQube, QtCreator, CLion, Eclipse CDT, Anjuta DevStudio и т.д. Быстрый старт (мониторинг компиляции). Прямая интеграция анализатора в системы автоматизации сборки и утилита BlameNotifier (рассылка писем). Режим автоматического анализа изменённых файлов. Почему нужны анализаторы кода?
Многим читателя понравилась моя статья "Последствия использования технологии Copy-Paste при программировании на Си++ и как с этим быть" [1]. Обратил на неё внимание и Scott Meyers [2] и задал вопрос о том, как же собственно статический анализ помог выявить описанные в статье ошибки.
Андрей Карпов
Вы узнаете, что такое статический анализ кода и историю его развития. Узнаете, как эффективно применять инструменты статического анализа в своей работе, увидите практические примеры использования этой методологии. Доклад ориентирован на программистов, использующих языки Си/Си++, но будет полезен всем
О сложностях программирования, или C# нас не спасет?Tatyanazaxarova
Программирование это сложно. С этим никто, надеюсь, не спорит. Но вот тема новых языков программирования, а точнее поиск "серебряной пули" всегда находит бурных отклик в умах разработчиков программного обеспечения. Особенно "модной" является тема превосходства одного языка программирования над другим. Ну, к примеру, что C# "круче", чем C++. И хотя holy wars – это не та причина, по которой я пишу этот пост, тем не менее, что называется "наболело". Ну не поможет C#/lisp/F#/Haskell/... написать изящное приложение, взаимодействующее с внешним миром, и все тут. Вся изящность теряется, стоит захотеть написать что-то реальное, а не пример "сам в себе".
Статический анализ кода: Что? Как? Зачем?Andrey Karpov
Методология статического анализа год за годом зарекомендовывает себя в поисках дефектов в исходном коде программ.
Максим расскажет про:
- методологию статического анализа и какие плюсы и минусы у нее есть;
- технологии этой методологии, которые позволяют выявлять разнообразнейшие дефекты в коде;
- интересные примеры ошибок в реальных проектах, которые были найдены при помощи статического анализа;
- интеграцию инструментов статического анализа в проекты любой сложности, и почему так важно регулярное использование подобных инструментов.
В предыдущей записи блога я обещал рассказать, почему сложно демонстрировать 64-битные ошибки на простых примерах. Разговор касался operator[] и я говорил, что в простых случая может работать даже явно некорректный код.
Статический анализ исходного кода на примере WinMergeTatyanazaxarova
Сегодня я хочу посвятить пост тематике, почему инструменты анализа исходного кода полезны вне зависимости от уровня знаний и опыта программиста. А польза такого анализа будет продемонстрирована на примере инструмента, который известен всем программистам - WinMerge.
Коллекция примеров 64-битных ошибок в реальных программахTatyanazaxarova
Статья представляет собой наиболее полную коллекцию примеров 64-битных ошибок на языках Си и Си++. Статья ориентирована на разработчиков Windows-приложений, использующих Visual C++, но будет полезна и более широкой аудитории.
Есть такая штука как инструментирование кода. Мало кто знает о ней, даже пользуясь результатами ее применения. Между тем, с инструментированием можно делать много всего интересного и, главное, полезного. Например, это может вам помочь лучше понять код или сделать процесс разработки более эффективным. Примеры инструментирования кода и принципы его работы.
Мы закончили рассмотрение паттернов 64-битных ошибок. Последнее на чем мы остановимся в связи с этими ошибками, является то, как они могут проявляться в программах.
Опыт разработки статического анализатора кодаAndrey Karpov
Один из основателей проекта PVS-Studio расскажет об опыте разработки статического анализатора кода C++. У инструментов статического анализа кода существует "проблема айсберга". От пользователей скрыты сложные механизмы анализа кода, и иногда им кажется, что статические анализаторы – это просто какие-то утилиты, ищущие опечатки с помощью регулярных выражений. Автор доклада постарается в общих чертах описать, как всё обстоит на самом деле. Он покажет на примерах, почему нормальный анализ с помощью регулярных выражений нереализуем, что такое Data Flow анализ, а также расскажет о других технологиях, применяемых при анализе кода. Вкратце будет затронут вопрос использования нейронных сетей, обсуждение которых сейчас является очень модной темой, и рассказано, почему с точки зрения анализа кода отношение к этому направлению является очень скептическим.
Как команда PVS-Studio может улучшить код операционной системы TizenAndrey Karpov
Цель: Контрактная работа команды PVS-Studio по исправлению ошибок и регулярному аудиту кода. Сейчас анализатор PVS-Studio выявляет более 10% ошибок, которые присутствуют в коде проекта Tizen. В случае регулярного использования PVS-Studio, в новом коде можно будет предотвратить около 20% ошибок.
Я прогнозирую, что команда PVS-Studio может на данный момент выявить и исправить около 27000 ошибок в проекте Tizen.
В лекции рассказано о доступных средствах по отладке веб-сайтов, их возможностях, а также способах их использования. Также речь пойдет о том, как искать ошибки у пользователей в продакшене и контролировать качество продукта.
Мы все допускаем ошибки при программировании и тратим массу времени на их устранение.
Один из методов который позволяет быстро диагностировать дефекты – статический анализ исходного кода.
Similar to Intel IPP Samples for Windows - работа над ошибками (20)
Урок 27. Особенности создания инсталляторов для 64-битного окруженияTatyanazaxarova
При разработке 64-битной версии приложения дополнительное внимание стоит уделить и вопросу дистрибуции программы. Ведь при установке на 64-битной операционной системе есть несколько нюансов, забыв о которых можно получить неработающий инсталляционный пакет.
После компиляции программы в 64-битном режиме она начинает потреблять большее количество памяти, чем ее 32-битный вариант. Часто это увеличение почти незаметно, но иногда потребление памяти может возрастать в 2 раза.
Урок 25. Практическое знакомство с паттернами 64-битных ошибокTatyanazaxarova
Данная статья содержит различные примеры 64-битных ошибок, собранные в демонстрационном примере PortSample. Однако, начиная с версии PVS-Studio 3.63, вместо PortSample в дистрибутив PVS-Studio включается более новая версия примеров, которая называется OmniSample. Поэтому некоторые скриншоты в статье не соответствуют актуальному состоянию дел.
Сам по себе рост размера структур не является ошибкой, но может приводить к потреблению необоснованного количества памяти и в результате к замедлению скорости работы программы. Будем рассматривать данный паттерн не как ошибку, но как причину неэффективности 64-битного кода.
Процессоры работают эффективнее, когда имеют дело с правильно выровненными данными. А некоторые процессоры вообще не умеют работать с не выровненными данными.
Генерирование и обработка исключений с участием целочисленных типов не является хорошей практикой программирования на языке Си++. Для этих целей следует использовать более информативные типы, например классы, производные от класса std::exception.
Урок 19. Паттерн 11. Сериализация и обмен даннымиTatyanazaxarova
Важным элементом переноса программного решения на новую платформу является преемственность к существующим протоколам обмена данными. Необходимо обеспечить чтение существующих форматов проектов, осуществлять обмен данными между 32-битными и 64-битными процессами и так далее.
Надеемся, вы уже успели отдохнуть от 13 урока и теперь сможете рассмотреть еще один важный паттерн ошибок, связанный с арифметическими выражениями, в которых участвуют типы различной размерности.
Урок 16. Паттерн 8. Memsize-типы в объединенияхTatyanazaxarova
Особенностью объединения (union) является то, что для всех элементов (членов) объединения выделяется одна и та же область памяти, то есть они перекрываются. Хотя доступ к этой области памяти возможен с использованием любого из элементов, элемент для этой цели должен выбираться так, чтобы полученный результат был осмысленным.
Большое количество ошибок при миграции на 64-битные системы связано с изменением соотношения между размером указателя и размером обычных целых. В среде с моделью данных ILP32 обычные целые и указатели имеют одинаковый размер. К сожалению, 32-битный код повсеместно опирается на это предположение. Указатели часто приводятся к int, unsigned, long, DWORD и другим неподходящим типам.
Мы специально выбрали номер "тринадцать" для этого урока, поскольку ошибки, связанные с адресной арифметикой в 64-битных системах, являются наиболее коварными. Надеемся, число 13 заставит вас быть внимательнее.
Урок 10. Паттерн 2. Функции с переменным количеством аргументовTatyanazaxarova
Классическими примерами, приводимыми во многих статьях по проблемам переноса программ на 64-битные системы, является некорректное использование функций printf, scanf и их разновидностей.
В некачественном коде часто встречаются магические числовые константы, наличие которых опасно само по себе. При миграции кода на 64-битную платформу эти константы могут сделать код неработоспособным, если участвуют в операциях вычисления адреса, размера объектов или в битовых операциях.
Урок 8. Статический анализ для выявления 64-битных ошибокTatyanazaxarova
Статический анализ кода - методология выявления ошибок в программном коде, основанная на просмотре программистом участков кода, помеченных статическим анализатором. Помеченные участки кода с большой вероятностью содержат ошибки определенного типа.
Исправление всех ошибок компиляции и предупреждений не будет означать работоспособность 64-битного приложения. И именно описанию и диагностике 64-битных ошибок будет посвящена основная часть уроков. Также не надейтесь на помощь ключа /Wp64, который многими часто без оснований преподносится при обсуждениях в форумах как чудесное средство поиска 64-битных ошибок.
Хочется сразу предупредить читателя, что невозможно всесторонне описать процесс сборки 64-битного приложения. Настройки любого проекта достаточно уникальны, поэтому к адаптации настроек для 64-битной системы всегда надо подходить внимательно. В уроке будут описаны только общие шаги, которые важны для любого проекта. Эти шаги подскажут вам с чего начать процесс.
Вначале следует убедиться, что используемая вами редакция Visual Studio позволяет собирать 64-битный код. Если вы планируете разрабатывать 64-битные приложения с использованием последней версии (на момент написания курса) Visual Studio 2008, то следующая таблица поможет определить, какая из редакций Visual Studio вам необходима.
PVS-Studio, решение для разработки современных ресурсоемких приложенийTatyanazaxarova
Инструмент PVS-Studio
набор правил Viva64 для анализа 64-битных приложений;
набор правил VivaMP для анализа параллельных приложений;
набор правил для анализа общего назначения.
Лицензионная и ценовая политика PVS-Studio
Информация о компании ООО «СиПроВер»
PVS-Studio, решение для разработки современных ресурсоемких приложений
Intel IPP Samples for Windows - работа над ошибками
1. Intel IPP Samples for Windows - работа
над ошибками
Автор: Андрей Карпов
Дата: 27.01.2011
Это моя очередная заметка о том, как PVS-Studio делает программы более надёжными. То есть
где, и какие ошибки он обнаруживает. На этот раз под молоток попали примеры,
демонстрирующие работу с библиотекой IPP 7.0 (Intel Performance Primitives Library).
В состав Intel Parallel Studio 2011 входит библиотека Performance Primitives Library. Эта библиотека
включает в себя большое количество примитивов, позволяющих создавать эффективные видео и
аудио кодеки, программы обработки сигналов, механизмы рендеринга изображений, архиваторы
и многое-многое другое. Естественно, с такой библиотекой работать не просто. Поэтому компания
Intel подготовила большое количество демонстрационных программ, построенных на основе этой
библиотеки. Вы можете познакомиться с описанием примеров и скачать их по следующим
ссылкам:
• Code Samples for the Intel Integrated Performance Primitives (Intel IPP) Library
• Intel Integrated Performance Primitives Library 7.0 Samples for Parallel Studio 2011
Все примеры разбиты на четыре группы:
• IPP Samples for Windows
• IPP UIC Demo for Windows
• IPP DMIP Samples for Windows
• IPP Cryptography Samples for Windows
В каждом наборе находится большое количество проектов, так что для начала я взял для
проверки только первый набор IPP Samples for Windows. Проверку я осуществил, используя PVS-
Studio версии 4.10.
Этим постом я хочу показать, что статический анализ полезен вне зависимости от
профессионализма программистов и уровня разрабатываемого решения. Идея "надо брать
профессионалов и писать сразу без ошибок" не работает. Даже высококвалифицированные
разработчики не застрахованы от ошибок и опечаток в процессе кодирования. Ошибки в
примерах для IPP очень хорошо это демонстрируют.
Подчеркну, что IPP Samples for Windows является высококачественным проектом. Однако, в силу
своего размера в 1.6 миллионов строк кода, он неизбежно содержит в себе разнообразнейшие
ошибки. Рассмотрим некоторые из них.
2. Неудачная замена индексов массива
Этот пример мог бы замечательно пополнить мою предыдущую статью "Последствия
использования технологии Copy-Paste при программировании на Си++ и как с этим быть":
struct AVS_MB_INFO
{
...
Ipp8u refIdx[AVS_DIRECTIONS][4];
...
};
void AVSCompressor::GetRefIndiciesBSlice(void){
...
if (m_pMbInfo->predType[0] & predType)
{
m_refIdx[iRefNum] = m_pMbInfo->refIdx[dir][0];
iRefNum += 1;
}
if (m_pMbInfo->predType[1] & predType)
{
m_refIdx[iRefNum] = m_pMbInfo->refIdx[dir][1];
iRefNum += 1;
}
if (m_pMbInfo->predType[2] & predType)
{
m_refIdx[iRefNum] = m_pMbInfo->refIdx[dir][2];
iRefNum += 1;
}
if (m_pMbInfo->predType[3] & predType)
{
m_refIdx[iRefNum] = m_pMbInfo->refIdx[dir][30];
3. iRefNum += 1;
}
...
}
Диагностическое сообщение PVS-Studio: V557 Array overrun is possible. The '30' index is pointing
beyond array bound. avs_enc umc_avs_enc_compressor_enc_b.cpp 495
Программист несколько раз скопировал фрагмент кода и изменил значение индексов массивов.
Но в самом конце его рука дрогнула. Он вписал число 3, но не удалил число 0. В результате
получился индекс 30 и при исполнении кода произойдет доступ далеко за границы массива.
Одинаковые ветви кода
Раз мы начали с копирования кода, то вот ещё один пример на эту тему:
AACStatus aacencGetFrame(...)
{
...
if (maxEn[0] > maxEn[1]) {
ics[1].num_window_groups = ics[0].num_window_groups;
for (g = 0; g < ics[0].num_window_groups; g++) {
ics[1].len_window_group[g] = ics[0].len_window_group[g];
}
} else {
ics[1].num_window_groups = ics[0].num_window_groups;
for (g = 0; g < ics[0].num_window_groups; g++) {
ics[1].len_window_group[g] = ics[0].len_window_group[g];
}
}
...
}
Диагностическое сообщение PVS-Studio: V523 The 'then' statement is equivalent to the 'else'
statement. aac_enc aac_enc_api_fp.c 1379
Но в этот раз, наоборот, забыли отредактировать скопированный код. Обе ветки условного
оператора "if" выполняют одни и те же действия.
4. Путаница с приоритетом операции декремента "--" и
разыменованием указателя "*"
static void
sbrencConflictResolution (..., Ipp32s *nLeftBord)
{
...
*nLeftBord = nBordNext - 1;
...
if (*lenBordNext > 1) {
...
*nLeftBord--;
}
...
}
Диагностическое сообщение PVS-Studio: V532 Consider inspecting the statement of '*pointer--'
pattern. Probably meant: '(*pointer)--'. aac_enc sbr_enc_frame_gen.c 428
Указатель "nLeftBord" использует для возврата значения из функции "sbrencConflictResolution".
Сначала по указанному адресу записывается значение "nBordNext - 1". При определенных
условиях это значение должно уменьшаться на единицу. Для уменьшения значения программист
использовал следующий код:
*nLeftBord--;
Ошибка в том, что вместо значения уменьшается сам указатель. Корректный код должен
выглядеть так:
(*nLeftBord)--;
Ещё более запутанная ситуация с операцией инкремента "++" и
разыменованием указателя "*"
Следующий код мне совершенно не понятен. Я не знаю, как его исправить, чтобы он приобрел
смысл. Возможно, здесь чего-то не хватает.
static IppStatus mp2_HuffmanTableInitAlloc(Ipp32s *tbl, ...)
{
...
for (i = 0; i < num_tbl; i++) {
5. *tbl++;
}
...
}
Диагностическое сообщение PVS-Studio: V532 Consider inspecting the statement of '*pointer++'
pattern. Probably meant: '(*pointer)++'. mpeg2_dec umc_mpeg2_dec.cpp 59
Сейчас, приведенный в примере цикл, эквивалентен следующему коду:
tbl += num_tbl;
Анализатор PVS-Studio предположил, что ,возможно, здесь забыты скобки и следовало написать
"(*tbl)++;". Но и этот код не имеет смысла. Тогда цикл будет эквивалентен:
*tbl += num_tbl;
В общем, очень странный какой-то цикл. Ошибка есть, но исправить ее, видимо, может только
автор кода.
Потеря признака, что произошла ошибка
В коде имеется функция "GetTrackByPidOrCreateNew", которая возвращает значение "-1" , если
возникает ошибка.
typedef signed int Ipp32s;
typedef unsigned int Ipp32u;
Ipp32s StreamParser::GetTrackByPidOrCreateNew(
Ipp32s iPid, bool *pIsNew)
{
...
else if (!pIsNew || m_uiTracks >= MAX_TRACK)
return -1;
...
}
С функцией "GetTrackByPidOrCreateNew" всё нормально. Но имеется ошибка при её
использовании:
Status StreamParser::GetNextData(MediaData *pData, Ipp32u *pTrack)
{
6. ...
*pTrack = GetTrackByPidOrCreateNew(m_pPacket->iPid, NULL);
if (*pTrack >= 0 && TRACK_LPCM == m_pInfo[*pTrack]->m_Type)
ippsSwapBytes_16u_I((Ipp16u *)pData->GetDataPointer(),
m_pPacket->uiSize / 2);
...
}
Диагностическое сообщение PVS-Studio: V547 Expression '* pTrack >= 0' is always true. Unsigned type
value is always >= 0. demuxer umc_stream_parser.cpp 179
Значение, которое возвращает функция "GetTrackByPidOrCreateNew" сохраняется в беззнаковом
формате (unsigned int). Это значит, что "-1" превратится в "4294967295", а условие "*pTrack >= 0"
всегда истинно.
В итоге, если функция "GetTrackByPidOrCreateNew " вернет значение "-1", то произойдет Access
Violation при выполнении кода "m_pInfo[*pTrack]->m_Type".
Copy-Paste и забытый +1
void H264SegmentDecoder::ResetDeblockingVariablesMBAFF()
{
...
if (GetMBFieldDecodingFlag(m_gmbinfo->mbs[m_CurMBAddr
- mb_width * 2]))
m_deblockingParams.nNeighbour[HORIZONTAL_DEBLOCKING] =
m_CurMBAddr - mb_width * 2;
else
m_deblockingParams.nNeighbour[HORIZONTAL_DEBLOCKING] =
m_CurMBAddr - mb_width * 2;
...
}
Диагностическое сообщение PVS-Studio: V523 The 'then' statement is equivalent to the 'else'
statement. h264_dec umc_h264_segment_decoder_deblocking_mbaff.cpp 340
Если посмотреть код рядом, то становится понятно, что в скопированной строчке забыли
прибавить единицу. Корректный код должен выглядеть так:
7. if (GetMBFieldDecodingFlag(m_gmbinfo->mbs[m_CurMBAddr
- mb_width * 2]))
m_deblockingParams.nNeighbour[HORIZONTAL_DEBLOCKING] =
m_CurMBAddr - mb_width * 2;
else
m_deblockingParams.nNeighbour[HORIZONTAL_DEBLOCKING] =
m_CurMBAddr - mb_width * 2 + 1;
Неподалеку в функции "H264CoreEncoder_ResetDeblockingVariablesMBAFF" есть ещё в точности
такая же ошибка с забытым "+ 1".
Диагностическое сообщение PVS-Studio: V523 The 'then' statement is equivalent to the 'else'
statement. h264_enc umc_h264_deblocking_mbaff_tmpl.cpp.h 366
Remove, который ничего не удаляет
void H264ThreadGroup::RemoveThread(H264Thread * thread)
{
AutomaticUMCMutex guard(m_mGuard);
std::remove(m_threads.begin(), m_threads.end(), thread);
}
Диагностическое сообщение PVS-Studio: V530 The return value of function 'remove' is required to be
utilized. h264_dec umc_h264_thread.cpp 226
Интересное сочетание. С одной стороны, всё солидно. Используется mutex, чтобы корректно
удалять элемент в многопоточном приложении. А с другой стороны, банально забыли, что
функция std::remove не удаляет элементы из массива, а только меняет их порядок. На самом деле
должно быть написано так:
m_threads .erase(
std::remove(m_threads.begin(), m_threads.end(), thread),
m_threads.end());
Сравнение полей структур с самими собой
Смотрю на ошибки и обратил внимание, что реализация стандарта сжатия видео H264 какая-то
глючноватая. К этому проекту относится достаточно большое количество найденных ошибок. Вот,
например, кто-то спешил при программировании и использовал сразу два неверных имени
переменных.
bool H264_AU_Stream::IsPictureSame(H264SliceHeaderParse & p_newHeader)
8. {
if ((p_newHeader.frame_num != m_lastSlice.frame_num) ||
(p_newHeader.pic_parameter_set_id !=
p_newHeader.pic_parameter_set_id) ||
(p_newHeader.field_pic_flag != p_newHeader.field_pic_flag) ||
(p_newHeader.bottom_field_flag != m_lastSlice.bottom_field_flag)
){
return false;
}
...
}
Диагностические сообщения PVS-Studio:
V501 There are identical sub-expressions 'p_newHeader.pic_parameter_set_id' to the left and to the
right of the '!=' operator. h264_spl umc_h264_au_stream.cpp 478
V501 There are identical sub-expressions 'p_newHeader.field_pic_flag' to the left and to the right of the
'!=' operator. h264_spl umc_h264_au_stream.cpp 479
Функция сравнения не работает, так как некоторые члены структуры сравниваются сами с собою.
Вот две исправленные строчки:
(p_newHeader.pic_parameter_set_id != m_lastSlice.pic_parameter_set_id)
(p_newHeader.field_pic_flag != m_lastSlice.field_pic_flag)
Некорректное копирование данных
Бывают ошибки использования не тех объектов, не только при сравнении, но и при копировании
состояний объектов:
Ipp32s ippVideoEncoderMPEG4::Init(mp4_Param *par)
{
...
VOL.sprite_width = par->sprite_width;
VOL.sprite_height = par->sprite_height;
VOL.sprite_left_coordinate = par->sprite_left_coordinate;
VOL.sprite_top_coordinate = par->sprite_left_coordinate;
9. ...
}
Диагностическое сообщение PVS-Studio: V537 Consider reviewing the correctness of
'sprite_left_coordinate' item's usage. mpeg4_enc mp4_enc_misc.cpp 387
В "VOL.sprite_top_coordinate" помещается неверное значение. Корректное присваивание должно
выглядеть так:
VOL.sprite_top_coordinate = par->sprite_top_coordinate;
Два цикла по одной переменной
JERRCODE CJPEGDecoder::DecodeScanBaselineNI(void)
{
...
for(c = 0; c < m_scan_ncomps; c++)
{
block = m_block_buffer + (DCTSIZE2*m_nblock*(j+(i*m_numxMCU)));
// skip any relevant components
for(c = 0; c < m_ccomp[m_curr_comp_no].m_comp_no; c++)
{
block += (DCTSIZE2*m_ccomp[c].m_nblocks);
}
...
}
Диагностическое сообщение PVS-Studio: V535 The variable 'c' is being used for this loop and for the
outer loop. jpegcodec jpegdec.cpp 4652
Для двух циклов, вложенных друг в друга, используется одна переменная 'c'. Результат работы
такой функции декодирования может быть весьма странным и неожиданным.
Двойное присваивание для большей надежности
H264EncoderFrameType*
H264ENC_MAKE_NAME(H264EncoderFrameList_findOldestToEncode)(...)
{
...
10. MaxBrefPOC =
H264ENC_MAKE_NAME(H264EncoderFrame_PicOrderCnt)(pCurr, 0, 3);
MaxBrefPOC =
H264ENC_MAKE_NAME(H264EncoderFrame_PicOrderCnt)(pCurr, 0, 3);
...
}
Диагностическое сообщение PVS-Studio: V519 The 'MaxBrefPOC' object is assigned values twice
successively. Perhaps this is a mistake. h264_enc umc_h264_enc_cpb_tmpl.cpp.h 784
Когда я увидел этот код, то мне вспомнился старый программистский анекдот:
- А почему у тебя в коде подряд два одинаковых GOTO стоят?
- А вдруг первый не сработает!
Данная ошибка, пожалуй, не критична, но все-таки это ошибка.
Код, который настораживает
AACStatus sbrencResampler_v2_32f(Ipp32f* pSrc, Ipp32f* pDst)
{
...
k = nCoef-1;
k = nCoef;
...
}
Диагностическое сообщение PVS-Studio: V519 The 'k' object is assigned values twice successively.
Perhaps this is a mistake. aac_enc sbr_enc_resampler_fp.c 90
Это двойное присваивание настораживает гораздо больше, чем в предыдущем примере. Такое
ощущение, что программист был не уверен в себе. Или решил, в начале, попробовать "nCoef-1", а
потом "nCoef". Ещё это называют "программированием методом эксперимента". И в любом
случае, это именно то место в коде, увидев которое следует задержаться и предаться
размышлениям.
Минимальное значение, которое не совсем минимально
void MeBase::MakeVlcTableDecision()
{
...
11. Ipp32s BestMV= IPP_MIN(IPP_MIN(m_cur.MvRate[0],m_cur.MvRate[1]),
IPP_MIN(m_cur.MvRate[2],m_cur.MvRate[3]));
Ipp32s BestAC= IPP_MIN(IPP_MIN(m_cur.AcRate[0],m_cur.AcRate[1]),
IPP_MIN(m_cur.AcRate[2],m_cur.AcRate[2]));
...
}
Диагностическое сообщение PVS-Studio: V501 There are identical sub-expressions to the left and to
the right of the '<' operator: (m_cur.AcRate [2]) < (m_cur.AcRate [2]) me umc_me.cpp 898
Вновь опечатка в индексе массива. Последний индекс должен быть 3, а не 2. Корректный вариант
кода:
Ipp32s BestAC= IPP_MIN(IPP_MIN(m_cur.AcRate[0],m_cur.AcRate[1]),
IPP_MIN(m_cur.AcRate[2],m_cur.AcRate[3]));
Подобные ошибки неприятны тем, что код "почти работает". Ошибка проявит себя только в том
случае, если минимальный элемент хранится в "m_cur.AcRate[3]". Такие ошибки любят проявлять
себя не при тестировании, а у пользователя на их наборах входных данных.
Максимальное значение, которое не совсем максимально
С максимальными значениями тоже не всегда ладится:
Ipp32s ippVideoEncoderMPEG4::Init(mp4_Param *par)
{
...
i = IPP_MAX(mBVOPsearchHorBack, mBVOPsearchHorBack);
...
}
Диагностическое сообщение PVS-Studio: V501 There are identical sub-expressions
'(mBVOPsearchHorBack)' to the left and to the right of the '>' operator. mpeg4_enc mp4_enc_misc.cpp
547
Два раза используется переменная mBVOPsearchHorBack. На самом деле планировалось
использовать mBVOPsearchHorBack и mBVOPsearchVerBack:
i = IPP_MAX(mBVOPsearchHorBack, mBVOPsearchVerBack);
12. Попадаем пальцем в небо
typedef struct
{
...
VM_ALIGN16_DECL(Ipp32f)
nb_short[2][3][__ALIGNED(MAX_PPT_SHORT)];
...
} mpaPsychoacousticBlock;
static void mp3encPsy_short_window(...)
{
...
if (win_counter == 0) {
nb_s = pBlock->nb_short[0][3];
}
...
}
Диагностическое сообщение PVS-Studio: V557 Array overrun is possible. The '3' index is pointing
beyond array bound. mp3_enc mp3enc_psychoacoustic_fp.c 726
Здесь видимо простая опечатка. Случайно использовали индекс '3' вместо '2'. Последствия,
думаю, понятны.
Ошибка, приводящая к замедлению скорости работы
void lNormalizeVector_32f_P3IM(Ipp32f *vec[3], Ipp32s* mask,
Ipp32s len) {
Ipp32s i;
Ipp32f norm;
for(i=0; i<len; i++) {
if(mask<0) continue;
norm = 1.0f/sqrt(vec[0][i]*vec[0][i]+
vec[1][i]*vec[1][i]+
13. vec[2][i]*vec[2][i]);
vec[0][i] *= norm; vec[1][i] *= norm; vec[2][i] *= norm;
}
}
Диагностическое сообщение PVS-Studio: V503 This is a nonsensical comparison: pointer < 0.
ipprsample ippr_sample.cpp 501
Это красивый пример кода, который из-за ошибки работает медленнее, чем мог бы. Алгоритм
должен нормализовать только те элементы, которые отмечены в массиве масок. Но приведенный
код делает нормализацию всех элементов. Ошибка находится в условии "if(mask<0)". Здесь
забыли использовать индекс "i". Указатель "mask" будет практически всегда больше или равен
нулю, а ,значит, мы обработаем все элементы.
Корректная проверка:
if(mask[i]<0) continue;
Результат вычитания всегда равен 0
int ec_fb_GetSubbandNum(void *stat)
{
_fbECState *state=(_fbECState *)stat;
return (state->freq-state->freq);
}
Диагностическое сообщение PVS-Studio: V501 There are identical sub-expressions to the left and to
the right of the '-' operator: state->freq - state->freq speech ec_fb.c 250
Здесь из-за опечатки функция всегда будет возвращать значение 0. Что-то не то мы здесь
вычитаем. Что нужно вычитать, я не знаю.
Некорректная обработка нехватки буфера
typedef unsigned int Ipp32u;
UMC::Status Init(..., Ipp32u memSize, ...)
{
...
memSize -= UMC::align_value<Ipp32u>(m_nFrames*sizeof(Frame));
if(memSize < 0)
return UMC::UMC_ERR_NOT_ENOUGH_BUFFER;
14. ...
}
Диагностическое сообщение PVS-Studio: V547 Expression 'memSize < 0' is always false. Unsigned type
value is never < 0. vc1_enc umc_vc1_enc_planes.h 200
Неверно, обрабатывается ситуация, когда размер буфера для работы недостаточен. Вместо
возврата кода ошибки программа продолжит работу и скорее всего, аварийно завершится. Дело в
том, что переменная "memSize" имеет тип "unsigned int". Следовательно, условие "memSize < 0"
всегда ложно и мы продолжаем работу с буфером недостаточного размера.
Наверное, это хороший пример уязвимости программы к атаке. Подсунув некорректные данные,
можно переполнить буфер и попробовать воспользоваться этим. Кстати, таких уязвимых мест
нашлось около 10. Приводить их не буду, чтобы не загромождать текст.
Некорректная проверка и как следствие выход за границу массива
Ipp32u m_iCurrMBIndex;
VC1EncoderMBInfo* VC1EncoderMBs::GetPevMBInfo(Ipp32s x, Ipp32s y)
{
Ipp32s row = (y>0)? m_iPrevRowIndex:m_iCurrRowIndex;
return ((m_iCurrMBIndex - x <0 || row <0)? 0 :
&m_MBInfo[row][m_iCurrMBIndex - x]);
}
Диагностическое сообщение PVS-Studio: V547 Expression 'm_iCurrMBIndex - x < 0' is always false.
Unsigned type value is never < 0. vc1_enc umc_vc1_enc_mb.cpp 188
Переменная "m_iCurrMBIndex" имеет тип "unsigned". Из-за этого выражение "m_iCurrMBIndex - x"
также имеет тип "unsigned". Следовательно, условие "m_iCurrMBIndex - x < 0" всегда ложно.
Рассмотрим последствия.
Пусть переменная "m_iCurrMBIndex" равна 5, а переменная "x" равна 10.
Выражение "m_iCurrMBIndex - x" равно 5u - 10i = 0xFFFFFFFBu.
Условие "m_iCurrMBIndex - x < 0" имеет значение false.
Выполняется выражение "m_MBInfo[row][0xFFFFFFFBu]" и происходит выход за границу массива.
Ошибка использования тернарного оператора '?:'.
Тернарный оператор достаточно опасен, так как легко допустить ошибку. Тем не менее,
программисты любят написать покороче и использовать интересную конструкцию языка. Язык
Си++ наказывает за это.
vm_file* vm_file_fopen(...)
15. {
...
mds[3] = FILE_ATTRIBUTE_NORMAL |
(islog == 0) ? 0 : FILE_FLAG_NO_BUFFERING;
...
}
Диагностическое сообщение PVS-Studio: V502 Perhaps the '?:' operator works in a different way than
it was expected. The '?:' operator has a lower priority than the '|' operator. vm vm_file_win.c 393
Код должен составлять комбинацию из флагов FILE_ATTRIBUTE_NORMAL и
FILE_FLAG_NO_BUFFERING. Но на самом деле элементу "mds[3]" всегда присваивается значение 0.
Программист забыл, что приоритет оператора "|" выше, чем приоритет оператора "?:".
Получается, что в коде написано следующее выражение (обратите внимание на скобки):
(FILE_ATTRIBUTE_NORMAL | (islog == 0)) ?
0 : FILE_FLAG_NO_BUFFERING;
Условие "FILE_ATTRIBUTE_NORMAL | (islog == 0)" всегда истинно и мы присваиваем элементу
"mds[3]" значение 0.
Корректное выражение должно выглядеть так (вновь обратите внимание на скобки):
FILE_ATTRIBUTE_NORMAL |
((islog == 0) ? 0 : FILE_FLAG_NO_BUFFERING);
Странная работа с массивом
AACStatus alsdecGetFrame(...)
{
...
for (i = 0; i < num; i++) {
...
*tmpPtr = (Ipp32s)((tmp << 24) + ((tmp & 0xff00) << 8) +
((tmp >> 8) & 0xff00) + (tmp >> 24));
*tmpPtr = *srcPrt;
...
}
...
16. }
Диагностическое сообщение PVS-Studio: V519 The '* tmpPtr' object is assigned values twice
successively. Perhaps this is a mistake. aac_dec als_dec_api.c 928
Предлагаю читателю самому изучить код и сделать выводы. Я опишу этот код только одним
словом - "оригинально".
Паранормальные присваивания
static
IPLStatus ownRemap8u_Pixel(...) {
...
saveXMask = xMap->maskROI;
saveXMask = NULL;
saveYMask = yMap->maskROI;
saveYMask = NULL;
...
}
Диагностические сообщения PVS-Studio:
V519 The 'saveXMask' object is assigned values twice successively. Perhaps this is a mistake. ipl
iplremap.c 36
V519 The 'saveYMask' object is assigned values twice successively. Perhaps this is a mistake. ipl
iplremap.c 38
Причина появления такого странного кода мне непонятна. Причем подобный блок повторяется в
разных функциях 8 раз!
Встречаются и другие подозрительные присваивания одной переменной:
Ipp32s ippVideoEncoderMPEG4::Init(mp4_Param *par)
{
...
mNumOfFrames = par->NumOfFrames;
mNumOfFrames = -1;
...
}
17. Диагностическое сообщение PVS-Studio: V519 The 'mNumOfFrames' object is assigned values twice
successively. Perhaps this is a mistake. mpeg4_enc mp4_enc_misc.cpp 276
Заключение
В этой статье перечислена только часть ошибок, обнаруженных мною в IPP Samples for Windows.
Некоторые ошибки я не привел, так как они являются братьями-близнецами тех примеров,
которые я рассмотрел в статье, и читать про них будет не интересно. Я не стал приводить здесь
несущественные ошибки. Примером может служить assert(), у которого из-за опечатки условие
всегда истинно. Многие участков кода я пропустил, так как просто не знаю, ошибка это или просто
некрасиво написано. Однако я думаю, что описанных дефектов достаточно, чтобы показать
сложность написания больших проектов даже профессиональными разработчиками.
Ещё раз сформулирую мысль, озвученную в начале статьи. Даже хороший программист не
застрахован от опечаток, забывчивости, желания сделать Copy-Paste и ошибок в логике. Я думаю,
в будущем ссылка на эту статью станет хорошим ответом людям, которые уверены что
произнесение фразы "надо правильно писать код" защитит их от любых ошибок.
Удачи всем в ваших C/C++/C++0x проектах. И желаю вам находить побольше ошибок, используя
так любимую мной методологию статического анализа.