Название: 19 смертных грехов, угрожающих безопасности программ
Автор: Майкл Ховард
Жанр: Программирование
isbn: 5-9706-0027-X
isbn:
Избегайте «хитроумного» кода – контроль целых должен быть прост и понятен. Вот пример чересчур заумного кода для контроля переполнения при сложении:
int a, b, c;
c = a + b;
if(a ^ b ^ c < 0)
return BAD_INPUT;
В этом фрагменте масса проблем. Многим из нас потребуется несколько минут на то, чтобы понять, что же автор хотел сделать. А кроме того, код дает ложные срабатывания – как позитивные, так и негативные, то есть работает не всегда. Вот другой пример проверки, дающей правильный результат не во всех случаях:
int a, b, c;
c = a * b;
if(c < 0)
return BAD_INPUT;
Даже если на входе допустимы только положительные числа, этот код все равно пропускает некоторые переполнения. Возьмем, к примеру, выражение (2 А 30 + + 1) * 8, то есть 2 А 33 + 8. После отбрасывания битов, вышедших за пределы 32 разрядов, получается 8. Это число положительно, а ошибка тем не менее есть. Безопаснее решить эту задачу, сохранив результат умножения 32–разрядных чисел в 64–разрядном, а затем проверить, равен ли хотя бы один из старших битов единице. Это и будет свидетельством переполнения.
Когда встречается подобный код:
unsigned a, b;
...
if (a * b < MAX) {
...
}
проще ограничить а и b значениями, произведение которых заведомо меньше МАХ. Например:
#include «limits.h»
#define MAX_A 10000
#define MAX_A 250
assert(UINT_MAX / MAX_A >= MAX_B); // проверим, что MAX_A и MAX_B
// достаточно малы
if (a < MAX_A && b < MAX_B) {
...
}
Если вы хотите надежно защитить свой код от переполнений целого, можете воспользоваться классом Safelnt, который написал Дэвид Лебланк (подробности в разделе «Другие ресурсы»). Но имейте в виду, что, не перехватывая исключения, возбуждаемые этим классом, вы обмениваете возможность выполнения произвольного кода на отказ от обслуживания. Вот пример использования класса Safelnt:
size_t CalcAllocSize(int HowMany, int Size, int HeaderLen)
{
try{
SafeInt<size_t> tmp(HowMany);
return tmp * Size + SafeInt<size_t>(HeaderLen);
}
catch(SafeIntException)
{
return (size_t)~0;
}
}
Целые со знаком используются в этом фрагменте только для иллюстрации; такую функцию следовало бы писать, пользуясь одним лишь типом size_t. Посмотрим, что происходит «под капотом». СКАЧАТЬ