|
|
|||
![]() PVS-Studio, статический анализатор кода для 64-битного и параллельного программирования на Си/Си++
|
|||
![]() ![]() ![]() ![]() ![]()
02.09.2010
Щупаем новый Intel Parallel Studio XE 2011 beta Вот, наконец, добрался попробовать Си++ компилятор, входящий в состав Intel Parallel Studio XE 2011 beta.»
30.08.2010
Пять дней на исправление ошибки в два символа, или миф о всемогущих технологиях при разработке программ В этом блоге нередко можно почитать о том, как тот или иной программный инструмент, или технология разработки программ помогает делать меньше ошибок, быстрее их находить, легче исправлять.»
30.08.2010
Д'Артаньян и интернет, или работа над проблемой битых ссылок Господа, хватит уже рассматривать ссылки исключительно в контексте их количества, купли/продажи и считать PR сайта, где они расположены.» ![]()
22.07.2010
Использование PVS-Studio вместе с системами continuous integration В статье показаны приемы организации работы анализатора кода PVS-Studio вместе с системами непрерывной интеграции (continuous integration).»
06.07.2010
Сравнение возможностей PVS-Studio и Visual Studio 2010 по выявлению дефектов в 64-битных программах В статье сравниваются три механизма анализа кода с точки зрения выявления 64-битных ошибок: компилятор Visual C++2010, компонент Code Analysis for C/C++ входящий в состав Visual Studio 2010 Premium/Ultimate и анализатор Viva64 входящий в состав PVS-Studio 3.60. »
29.06.2010
Коллекция примеров 64-битных ошибок в реальных программах
Статья представляет собой наиболее полную коллекцию примеров 64-битных ошибок на языках Си и Си++. » ![]() |
64-битные уроки![]() Урок 11. Паттерн 3. Операции сдвигаЛегко сделать ошибку в коде, работающем с отдельными битами. Рассматриваемый паттерн 64-битных ошибок связан с операциями сдвига. Пример кода: ptrdiff_t SetBitN(ptrdiff_t value, unsigned bitNum) {
ptrdiff_t mask = 1 << bitNum;
return value | mask;
}
Приведенный код работоспособен на 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: ptrdiff_t mask = ptrdiff_t(1) << bitNum; Заметим также, что неисправленный код приведет еще к одной интересной ошибке. При выставлении 31 бита на 64-битной системе результатом работы функции будет значение 0xffffffff80000000 (см. рисунок 2). Результатом выражения 1 << 31 является отрицательное число -2147483648. Это число представляется в 64-битной целой переменной как 0xffffffff80000000. Рисунок 2 - Ошибка установки 31-ого бита на 64-битной системе. Следует помнить и учитывать эффекты сдвига значений различных типов. Для лучшего понимания и наглядности изложенной информации в таблице 1 приведен ряд интересных выражений со сдвигами в 64-битной системе. Таблица 1 - Выражения со сдвигами и результаты в 64-битной системе (использовался компилятор Visual C++ 2005) Описанный вид ошибок можно считать опасным не только с точки зрения корректности работы программы, но и с точки зрения безопасности. Потенциально манипулируя с входными данными подобных некорректных функций можно получить недопустимо высокие права, если, например, происходит обработка масок прав доступа, заданных отдельными битами. Вопросы, связанные с использованием ошибок в 64-битном коде для взлома и компрометации приложений затронуты нами в статье "Безопасность 64-битного кода". Рассмотрим теперь более тонкий пример: struct BitFieldStruct {
unsigned short a:15;
unsigned short b:13;
};
BitFieldStruct obj;
obj.a = 0x4000;
size_t addr = obj.a << 17; //Sign Extension
printf("addr 0x%Ix\n", addr);
//Output on 32-bit system: 0x80000000
//Output on 64-bit system: 0xffffffff80000000
В 32-битной среде порядок вычисления выражения будет выглядеть, как показано на рисунке 3. Рисунок 3 - Вычисление выражения в 32-битном коде Обратим внимание, что при вычислении выражения "obj.a << 17" происходит знаковое расширение типа unsigned short до типа int. Более наглядно это может продемонстрировать следующий код: #include <stdio.h>
template <typename T> void PrintType(T)
{
printf("type is %s %d-bit\n",
(T)-1 < 0 ? "signed" : "unsigned", sizeof(T)*8);
}
struct BitFieldStruct {
unsigned short a:15;
unsigned short b:13;
};
int main(void)
{
BitFieldStruct bf;
PrintType( bf.a );
PrintType( bf.a << 2);
return 0;
}
Result:
type is unsigned 16-bit
type is signed 32-bit
Теперь посмотрим, к чему приводит наличие знакового расширения в 64-битном коде. Последовательность вычисления выражения показана на рисунке 4. Рисунок 4 - Вычисление выражения в 64-битном коде. Член структуры obj.a преобразуется из битового поля типа unsigned short в int. Выражение "obj.a << 17" имеет тип int, но оно преобразуется в ptrdiff_t и затем в size_t перед тем как будет присвоено переменной addr. В результате мы получим значение 0xffffffff80000000, вместо ожидаемого 0x0000000080000000. Будьте внимательны при работе с битовыми полями. Для предотвращения описанной ситуации в нашем примере достаточно явно привести obj.a к типу size_t. ...
size_t addr = size_t(obj.a) << 17;
printf("addr 0x%Ix\n", addr);
//Output on 32-bit system: 0x80000000
//Output on 64-bit system: 0x80000000
ДиагностикаПотенциально опасные сдвиги будут выявлены статическим анализатором 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. | ||