openlang

Почему компилятор удалил мою проверку знакового переполнения?

if (x + 1 < x) для знакового int не защищает от переполнения. Знаковое переполнение в C является неопределённым поведением, поэтому компилятор вправе считать, что x + 1 никогда не переполняется, решить, что условие ложно, и выбросить ветку. Распространённые компиляторы так и делают, как только включена оптимизация.

Это проблема безопасности, потому что исчезает обычно та самая проверка, что стоит перед длиной для malloc или memcpy. На ревью исходник выглядит безопасным. В бинарнике проверки нет.

Проверяйте до операции. if (x > INT_MAX - 1) для простого случая. Для сложения в общем виде есть __builtin_add_overflow в GCC и Clang или ckd_add из <stdckdint.h>, если ваш инструментарий поддерживает C23. -fwrapv заставляет знаковое переполнение заворачиваться на компиляторах, где этот флаг есть. Как смягчение годится, но это не переносимый C. А беззнаковая арифметика вроде size_t заворачивается по определению, что означает лишь корректно определённую неверную длину, так что и там сравнивайте с пределом явно.

Правило занимает одно предложение в разделе 6.5 стандарта. Им объясняется немало баг-репортов, обвиняющих оптимизатор.

Разговор продолжается 1

Следуя доводам
  1. Ссылка на ответ

    Swift пошёл другим путём, и мне это нравится. + для Int при переполнении аварийно останавливает программу, так что неверная длина приводит к падению, а не проскакивает дальше. Хотите заворачивание, скажите об этом явно через &+, а addingReportingOverflow возвращает результат вместе с флагом, если лучше проверить, чем упасть. Падение тоже не бесплатно. Но его гораздо легче найти, чем проверку, которая тихо исчезла. (Если только кто-то не собирает с -Ounchecked, тогда этих проверок нет.)

Другие стороны того же вопроса

  1. Нужно ли правилу allow в Rego значение false по умолчанию?

    {"approved": false} и {} получают от allow if input.approved == true один и тот же ответ. И этот ответ undefined, а не false.…

    Ответы 2regosql