Для получения триального ключа
заполните форму ниже
Team License (базовая версия)
Enterprise License (расширенная версия)
* Нажимая на кнопку, вы даете согласие на обработку
своих персональных данных. См. Политику конфиденциальности

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

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

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

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

** На сайте установлена reCAPTCHA и применяются
Политика конфиденциальности и Условия использования Google.
Ваше сообщение отправлено.

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


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

>
>
>
Как обнаружить переполнение 32-битных п…

Как обнаружить переполнение 32-битных переменных в длинных циклах в 64-битной программе?

22 Мар 2016

Одна из проблем, с которой сталкиваются разработчики 64-битных приложений, это переполнение 32-битных переменных в очень длинных циклах. С этой задачей хорошо справляется анализатор кода PVS-Studio (набор диагностик Viva64). На тему переполнения переменных в циклах есть ряд вопросов на сайте StackOverflow.com. Но поскольку мои ответы могут счесть исключительно рекламными, а не как полезную информацию, я решил описать возможности PVS-Studio в статье.

Типовой конструкцией языка C/C++ является цикл. При портирования программ на 64-битную архитектуру, циклы неожиданно становятся слабым местом, так как при разработке кода редко кто заранее задумывался, что произойдет, если программе придётся выполнять миллиарды итераций.

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

Итак, проблема. В 64-битной программе происходит переполнение целочисленных 32-битных типов. Речь идёт о таких типах как int, unsigned, long (если это Win64). Необходимо как-то выявить все такие опасные места. Это может сделать анализатор PVS-Studio, о чем мы и поговорим.

Рассмотрим различные варианта переполнения переменных, связанных с длинными циклами.

Первая ситуация. Описана на сайте StackOverflow здесь: "How can elusive 64-bit portability issues be detected?". Имеется код следующего вида:

int n;
size_t pos, npos;
/* ... initialization ... */
while((pos = find(ch, start)) != npos)
{
    /* ... advance start position ... */
    n++; // this will overflow if the loop iterates too many times
}

Программа обрабатывает очень длинные строки. В 32-битной программе длина строка не сможет превысить INT_MAX. Поэтому никакой ошибки произойти не может. Да, программа не может обработать какие-то большие объемы данных, но это не ошибка, а ограничение возможностей 32-битной архитектуры.

В 64-битной программе длина строки уже может быть больше INT_MAX и соответственно переменная n может переполниться. Это приведёт к неопределённому поведению программы. Не надо думать, что переполнение просто превратит число 2147483647 в -2147483648. Это именно неопределённое поведение и предсказать последствия невозможно. Для тех, кто не верит, что переполнение знаковой переменной приводит к неожиданным изменениям в работе программы, предлагаю познакомиться с моей статьёй "Undefined behavior ближе, чем вы думаете".

Итак, нужно обнаружить, что переменная n может переполниться. Нет ничего проще. Запускаем PVS-Studio и получаем предупреждение:

V127 An overflow of the 32-bit 'n' variable is possible inside a long cycle which utilizes a memsize-type loop counter. mfcapplication2dlg.cpp 190

Если изменить тип переменной n на size_t, то ошибка, а соответственно и сообщение анализатора, исчезнет.

Там же приводится ещё один пример кода, который требуется выявить:

int i = 0;
for (iter = c.begin(); iter != c.end(); iter++, i++)
{
    /* ... */
}

Запускаем PVS-Studio и вновь получаем предупреждение V127:

V127 An overflow of the 32-bit 'i' variable is possible inside a long cycle which utilizes a memsize-type loop counter. mfcapplication2dlg.cpp 201

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

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

Отмечу также, что мы имеем опыт портирования большого проекта в 9 млн. строк кода на 64-битную платформу. И PVS-Studio отлично показал себя в работе над этим проектом.

Перейдем к следующей теме на сайте StackOverflow: "Can Klocwork (or other tools) be aware of types, typedefs and #define directives?".

Как я понимаю, человек поставил для себя задачу найти подходящий инструмент для поиска всех циклов, организованных с помощью 32-битных счетчиков цикла. Т.е. другими словами где используется тип int.

Это задача несколько отличается от предыдущей. Но такие циклы действительно следует искать. Ведь с помощью переменной int невозможно обрабатывать огромные массивы и так далее.

Подошел человек к решению задачи неправильно. В этом он не виноват. Он просто не знает о существовании PVS-Studio. Сейчас вы поймете почему я так говорю.

Итак, он планирует искать:

for (int i = 0; i < 10; i++)
    // ...

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

Править все циклы подряд, заменяя int, например, на intptr_t тоже плохой вариант. Это очень много работы и изменений в коде.

Анализатор PVS-Studio может помочь. Приведённый выше цикл он не найдёт. Потому, что его и не надо искать. В нём просто нет места для ошибки. Цикл выполняет 10 итераций. И никакого переполнения в нем быть не может. Так что нечего программисту тратить время на этот участок кода.

Зато анализатор укажет вот на такие циклы:

void Foo(std::vector<float> &v)
{
  for (int i = 0; i < v.size(); i++)
    v[i] = 1.0;
}

Анализатор выдаст сразу 2 предупреждения. Первое предупреждает о том, что в выражении 32-битный тип сравнивается с memsize-типом:

V104 Implicit conversion of 'i' to memsize type in an arithmetic expression: i < v.size() mfcapplication2dlg.cpp 210

И действительно, тип переменной i не подходит для организации длинных циклов.

Второе предупреждение говорит, что странно в качестве индекса использовать 32-битную переменную. Если массив большой, то код ошибочен.

V108 Incorrect index type: v[not a memsize-type]. Use memsize type instead. mfcapplication2dlg.cpp 211

Корректный код должен выглядеть так:

void Foo(std::vector<float> &v)
{
  for (std::vector<float>::size_type i = 0; i < v.size(); i++)
    v[i] = 1.0;
}

Код стал длинным и некрасивым, поэтому появляется соблазн использовать ключевое слово auto, но этого делать нельзя - измененный таким образом код вновь некорректен:

for (auto i = 0; i < v.size(); i++)
  v[i] = 1.0;

Так как константа 0 имеет тип int, то и переменная i будет иметь тип int. И мы вернулись к тому, с чего начали. Кстати раз зашла речь о новых возможностях стандарта языка С++, предлагаю взглянуть на статью "C++11 и 64-битные ошибки".

Думаю, можно пойти на компромисс и написать не идеальный, но правильный код:

for (size_t i = 0; i < v.size(); i++)
  v[i] = 1.0;

Примечание. Конечно, ещё более правильным будет использовать итераторы или алгоритм fill(). Но мы говорим о поисках переполнения 32-битных переменных в старых программах. Поэтому я и не рассматриваю такие варианты исправления кода. Это уже совсем другая тема.

Хочу подчеркнуть, что анализатор достаточно умён и старается почем зря не беспокоить программиста. Например, он не будет выдавать предупреждения, если увидит, что обрабатывается маленький массив:

void Foo(int n)
{
  float A[100];
  for (int i = 0; i < n; i++)
    A[i] = 1.0;
}

Заключение

Анализатор PVS-Studio является лидером по поиску 64-битных ошибок. Изначально, он как раз и создавался для помощи программистам в портирования их программ на 64-битные системы. В то время он ещё назывался Viva64. Это уже потом, он превратился в анализатор общего назначения, но существовавшие 64-битные диагностики никуда не исчезли и всё также готовы вам помочь.

Скачать демонстрационную версию можно здесь.

Подробнее о разработке 64-битных программ.

Популярные статьи по теме
Бесплатный PVS-Studio для тех, кто развивает открытые проекты

Дата: 22 Дек 2018

Автор: Андрей Карпов

В канун празднования нового 2019 года команда PVS-Studio решила сделать приятный подарок всем контрибьюторам open-source проектов, хостящихся на GitHub, GitLab или Bitbucket. Им предоставляется возмо…
Статический анализ как часть процесса разработки Unreal Engine

Дата: 27 Июн 2017

Автор: Андрей Карпов

Проект Unreal Engine развивается - добавляется новый код и изменятся уже написанный. Неизбежное следствие развития проекта - появление в коде новых ошибок, которые желательно выявлять как можно раньш…
PVS-Studio ROI

Дата: 30 Янв 2019

Автор: Андрей Карпов

Время от времени нам задают вопрос, какую пользу в денежном эквиваленте получит компания от использования анализатора PVS-Studio. Мы решили оформить ответ в виде статьи и привести таблицы, которые по…
Характеристики анализатора PVS-Studio на примере EFL Core Libraries, 10-15% ложных срабатываний

Дата: 31 Июл 2017

Автор: Андрей Карпов

После большой статьи про проверку операционной системы Tizen мне было задано много вопросов о проценте ложных срабатываний и о плотности ошибок (сколько ошибок PVS-Studio выявляет на 1000 строк кода)…
Как и почему статические анализаторы борются с ложными срабатываниями

Дата: 20 Мар 2017

Автор: Андрей Карпов

В своей предыдущей статье я писал, что мне не нравится подход, при котором статические анализаторы кода оцениваются с помощью синтетических тестов. В статье приводился пример, воспринимаемый анализат…
Главный вопрос программирования, рефакторинга и всего такого

Дата: 14 Апр 2016

Автор: Андрей Карпов

Вы угадали, ответ - "42". Здесь приводится 42 рекомендации по программированию, которые помогут избежать множества ошибок, сэкономить время и нервы. Автором рекомендаций выступает Андрей Карпов - тех…
Зло живёт в функциях сравнения

Дата: 19 Май 2017

Автор: Андрей Карпов

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

Дата: 21 Ноя 2018

Автор: Андрей Карпов

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

Дата: 31 Май 2014

Автор: Андрей Карпов

Я изучил множество ошибок, возникающих в результате копирования кода. И утверждаю, что чаще всего ошибки допускают в последнем фрагменте однотипного кода. Ранее я не встречал в книгах описания этого …
Любите статический анализ кода!

Дата: 16 Окт 2017

Автор: Андрей Карпов

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

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

Следующие комментарии

На сайте установлена reCAPTCHA и применяются
Политика конфиденциальности и Условия использования Google.
Этот сайт использует куки и другие технологии, чтобы предоставить вам более персонализированный опыт. Продолжая просмотр страниц нашего веб-сайта, вы принимаете условия использования этих файлов. Если вы не хотите, чтобы ваши данные обрабатывались, пожалуйста, покиньте данный сайт. Подробнее →
Принять