![]() PVS-Studio, статический анализатор кода для 64-битного и параллельного программирования на Си/Си++
|
||||||
![]() ![]() ![]() ![]() ![]()
11.03.2010
Параллельные заметки N4 - продолжаем знакомиться с конструкциями OpenMP Продолжим знакомство с технологией OpenMP и рассмотрим некоторые функции и новые директивы.»
02.03.2010
Параллельные заметки №3 - базовые конструкции OpenMP Мы начинаем знакомить вас непосредственно с использованием технологии OpenMP и рассмотрим в этой заметке некоторые базовые конструкции.»
28.02.2010
Как стандарт C++0x поможет в борьбе с 64-битными ошибками Программисты видят в стандарте C++0x возможность использовать лямбда-функции и прочие мало понятные для меня сущности :).» ![]()
10.12.2009
Вопросы и ответы по PVS-Studio (PVS-Studio FAQ) В документе собраны некоторые вопросы и ответы по анализатору кода PVS-Studio компании ООО "СиПроВер".»
09.12.2009
Вопросы и ответы по библиотеке VivaCore (VivaCore FAQ) В документе собраны некоторые вопросы и ответы по библиотеке анализа Си/Си++ кода VivaCore компании ООО "СиПроВер".»
23.11.2009
PVS-Studio: использование функции "Mark as False Alarm"
В статье приведены описание и пример использования новой функции PVS-Studio 3.40 "Mark as False Alarm" ("Пометить как ложное срабатывание").» ![]() |
64-битные уроки![]() Урок 11. Паттерн 3. Операции сдвига
Легко сделать ошибку в коде, работающем с отдельными битами. Рассматриваемый паттерн 64-битных ошибок связан с операциями сдвига. Пример кода:
Приведенный код работоспособен на 32-битной архитектуре и позволяет выставлять бит с номерами от 0 до 31 в единицу. После переноса программы на 64-битную платформу возникнет необходимость выставлять биты от 0 до 63. Но данный код никогда не выставит биты, с номерами 32-63. Обратите внимание, что числовой литерал "1" имеет тип int, и при сдвиге на 32 позиции произойдет переполнение, как показано на рисунке 1. Получим мы в результате 0 (рисунок 1-B) или 1 (рисунок 1-C) - зависит от реализации компилятора. ![]() Рисунок 1 – a) корректная установка 31-ого бита в 32-битном коде; b,c) - Ошибка установки 32-ого бита на 64-битной системе (два варианта поведения)
Для исправления кода необходимо сделать константу "1" того же типа, что и переменная mask:
Заметим также, что неисправленный код приведет еще к одной интересной ошибке. При выставлении 31 бита на 64-битной системе результатом работы функции будет значение 0xffffffff80000000 (см. рисунок 2). Результатом выражения 1 << 31 является отрицательное число -2147483648. Это число представляется в 64-битной целой переменной как 0xffffffff80000000. ![]() Рисунок 2 - Ошибка установки 31-ого бита на 64-битной системе.
Следует помнить и учитывать эффекты сдвига значений различных типов. Для лучшего понимания и наглядности изложенной информации в таблице 1 приведен ряд интересных выражений со сдвигами в 64-битной системе. ![]() Таблица 1 - Выражения со сдвигами и результаты в 64-битной системе (использовался компилятор Visual C++ 2005)
Описанный вид ошибок можно считать опасным не только с точки зрения корректности работы программы, но и с точки зрения безопасности. Потенциально манипулируя с входными данными подобных некорректных функций можно получить недопустимо высокие права, если например, происходит обработка масок прав доступа, заданных отдельными битами. Вопросы, связанные с использованием ошибок в 64-битном коде для взлома и компрометации приложений затронуты нами в статье "Безопасность 64-битного кода".
Рассмотрим теперь более тонкий пример:
В 32-битной среде порядок вычисления выражения будет выглядеть, как показано на рисунке 3. ![]() Рисунок 3 - Вычисление выражения "obj.a << 17" в 32-битном коде
Обратим внимание, что при вычислении выражения "obj.a << 17" происходит знаковое расширение типа unsigned short до типа int. Более наглядно, это может продемонстрировать следующий код:
Теперь посмотрим, к чему приводит наличие знакового расширения в 64-битном коде. Последовательность вычисления выражения показана на рисунке 4. ![]() Рисунок 4 - Вычисление выражения "obj.a << 17" в 64-битном коде.
Член структуры obj.a преобразуется из битового поля типа unsigned short в int. Выражение "obj.a << 17" имеет тип int, но оно преобразуется в ptrdiff_t и затем в size_t, перед тем как будет присвоено переменной addr. В результате мы получим число значение 0xffffffff80000000, вместо ожидаемого значения 0x0000000080000000. Будьте внимательны при работе с битовыми полями. Для предотвращения описанной ситуации в нашем примере достаточно явно привести obj.a к типу size_t.
ДиагностикаПотенциально опасные сдвиги будут выявлены статическим анализатором PVS-Studio, когда он обнаружит неявное расширение 32-битного типа до memsize типа. Анализатор предупредит об опасной конструкции диагностическим сообщением V101. При этом сама по себе операция сдвига подозрения не вызывает. Но анализатор обнаруживает неявное расширение типа int до типа memsize при присваивании. И информирует об этом программиста, который может обнаружить ошибку в коде. Соответственно, если расширения нет, то код считается анализатором безопасным. Пример: "int mask = 1 << bitNum;".
Авторы курса: Андрей Карпов (karpov@viva64.com), Евгений Рыжков (evg@viva64.com). Правообладателем курса "Уроки разработки 64-битных приложений на языке Си/Си++" является ООО "Системы программной верификации". Компания занимается разработкой программного обеспечения в области анализа исходного кода программ. Сайт компании: http://www.viva64.com. Контактная информация: e-mail: support@viva64.com, 300027, г. Тула, а/я 1800. | |||||