Top.Mail.Ru
Unicorn with delicious cookie
Мы используем куки, чтобы пользоваться сайтом было удобно.
Хорошо
to the top
>
>
>
V1083. Signed integer overflow in...
menu mobile close menu
Проверка проектов
Дополнительная информация
toggle menu Оглавление

V1083. Signed integer overflow in arithmetic expression. This leads to undefined behavior.

13 Май 2022

Анализатор обнаружил арифметическое выражение, в котором может произойти переполнение знакового числа.

Рассмотрим пример:

long long foo()
{
  long longOperand = 0x7FFF'FFFF;
  long long y = longOperand * 0xFFFF;
  return y;
}

По правилам C и C++ результирующим типом выражения longOperand * 0xFFFF будет long. При использовании компилятора MSVC на Windows тип long имеет размер 4 байта. Максимальное значение, которое может быть представлено этим типом, равно 2'147'483'647 в десятичной системе или 0x7FFF'FFFF в шестнадцатеричной. При умножении переменной longOperand на 0xFFFF (65 535) ожидается результат 0x7FFF'7FFF'0001. Однако согласно стандарту C (см. стандарт С18 пункт 6.5 параграф 5) и C++ (см. стандарт С++20 пункт 7.1 параграф 4) переполнение знаковых чисел приводит к неопределённому поведению.

Исправить этот код можно несколькими способами.

Если требуется произвести корректные вычисления, необходимо использовать типы, размеры которых будут достаточны для отображения чисел. Если число не помещается в машинное слово, то можно воспользоваться одной из библиотек для работы с длинной арифметикой. Например, GMP, MPRF, cnl.

Пример выше можно исправить следующим образом:

long long foo()
{
  long longOperand = 0x7FFF'FFFF;
  long long y = static_cast<long long>(longOperand) * 0xFFFF;
  return y;
}

Если переполнение знаковых чисел является неожидаемым поведением и требует обработки, то можно воспользоваться специальными библиотеками для безопасной работы с числами. Например, boost::safe_numerics или Google Integers.

Если требуется реализовать циклическую арифметику для знаковых чисел с определённым по стандарту поведением, то для расчётов можно воспользоваться беззнаковыми числами. В случае их переполнения происходит "оборачивание" числа по модулю 2 ^ n, где n — количество бит в числе.

Рассмотрим одно из возможных решений на основе std::bit_cast (C++20):

#include <concepts>
#include <type_traits>
#include <bit>
#include <functional>

namespace detail
{
  template <std::signed_integral R,
            std::signed_integral T1,
            std::signed_integral T2,
            std::invocable<std::make_unsigned_t<T1>,
                           std::make_unsigned_t<T2>> Fn>
  R safe_signed_wrapper(T1 lhs, T2 rhs, Fn &&op)
    noexcept(std::is_nothrow_invocable_v<Fn,
                                         std::make_unsigned_t<T1>,
                                         std::make_unsigned_t<T2>>)
  {
    auto uLhs = std::bit_cast<std::make_unsigned_t<T1>>(lhs);
    auto uRhs = std::bit_cast<std::make_unsigned_t<T2>>(rhs);

    auto res = std::invoke(std::forward<Fn>(op), uLhs, uRhs);

    using UR = std::make_unsigned_t<R>;
    return std::bit_cast<R>(static_cast<UR>(res));
  }
}

Функция std::bit_cast приводит lhs' и 'rhs к соответствующим беззнаковым представлениям. Далее на двух преобразованных операндах выполняется некоторая арифметическая операция. Затем результат расширяется или сужается до нужного результирующего типа и превращается в знаковый.

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

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

Рассмотрим пример подробнее:

bool is_max_int(int32_t a)
{
  return a + 1 < a;
}

Если a равно MAX_INT, то условие a + 1 < a будет равно false. Таким образом часто проверяют не произошло ли переполнение.

Однако компилятор генерирует такой код:

is_max_int(int):                        # @is_max_int(int)
        xor     eax, eax
        ret

Инструкция ассемблера xor eax, eax обнуляет результат выполнения функции is_max_int. В результате последняя всегда возвращает true вне зависимости от значения a. В данном случае это результат неопределённого поведения при переполнении.

В случае применения беззнакового представления такого не происходит:

is_max_int(int):                        # @is_max_int(int)
        cmp     edi, 2147483647
        sete    al
        ret

Компилятор сгенерировал код, который проверяет условие.

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

Для активации этой настройки необходимо добавить следующий комментарий в исходный код или в файл конфигурации диагностических правил .pvsconfig:

//+V1083 CHECK_UNSIGNED_OVERFLOW:YES

Чтобы отключить настройку, используйте комментарий:

//+V1083 CHECK_UNSIGNED_OVERFLOW:NO

Выявляемые диагностикой ошибки классифицируются согласно ГОСТ Р 71207–2024 как критические и относятся к типу: Ошибки целочисленного переполнения и некорректного совместного использования знаковых и беззнаковых чисел.

Данная диагностика классифицируется как:

Взгляните на примеры ошибок, обнаруженных с помощью диагностики V1083.

close form

Заполните форму в два простых шага ниже:

Ваши контактные данные:

Шаг 1
Поздравляем! У вас есть промокод!

Тип желаемой лицензии:

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

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

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

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

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

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


Если вы так и не получили ответ, пожалуйста, проверьте, отфильтровано ли письмо в одну из следующих стандартных папок:

  • Промоакции
  • Оповещения
  • Спам