V5006. OWASP. More than N bits are required to store the value, but the expression evaluates to the T type which can only hold K bits.
Анализатор обнаружил потенциальную ошибку в выражении, где используются операции сдвига (shift operations). В процессе сдвига возникает переполнение, и значения старших бит будут потеряны.
Для начала рассмотрим эту ситуацию на простом примере:
std::cout << (77u << 26);
Значение выражения 77u << 26
равно 5167382528 (0x134000000). При этом выражение 77u << 26
имеет тип unsigned int
. Это значит, что старшие биты будут отброшены, и будет напечатано значение 872415232 (0x34000000)
.
Переполнения, возникающие при сдвигах, часто указывают на наличие логической ошибки или просто опечатки. Например, может быть, что число 77u
хотели задать в восьмеричной системе счисления. Тогда корректный код должен выглядеть так:
std::cout << (077u << 26);
Здесь переполнения уже не возникает. Значение выражения 077u << 26
равно 4227858432 (0xFC000000)
.
Если хочется распечатать значение 5167382528
, то число 77 должно быть задано с помощью 64-битного типа. Например, так:
std::cout << (77ui64 << 26);
Перейдём к случаям, с которыми можно встретиться на практике. Для этого рассмотрим две ошибки, обнаруженные в реальных приложениях.
Первый пример.
typedef __UINT64 Ipp64u;
#define MAX_SAD 0x07FFFFFF
....
Ipp64u uSmallestSAD;
uSmallestSAD = ((Ipp64u)(MAX_SAD<<8));
Программист хотел записать в 64-битную переменную uSmallestSAD
значение 0x7FFFFFF00
. Но на самом деле переменная будет иметь значение 0xFFFFFF00. Старшие биты будут отброшены, так как выражение MAX_SAD<<8
имеет тип int
. Программист знал про это, поэтому решил использовать явное приведение типа, но ошибся, расставляя скобки. Этот пример хорошо демонстрирует, что подобные ошибки могут возникать из-за простой опечатки. Корректный код:
uSmallestSAD = ((Ipp64u)(MAX_SAD))<<8;
Второй пример.
#define MAKE_HRESULT(sev,fac,code) \
((HRESULT) \
(((unsigned long)(sev)<<31) | \
((unsigned long)(fac)<<16) | \
((unsigned long)(code))) )
*hrCode = MAKE_HRESULT(3, FACILITY_ITF, messageID);
Функция должна сформировать информацию об ошибке в переменной типа HRESULT
. Для этого программист использовал макрос MAKE_HRESULT
, но сделал это неправильно, посчитав, что первый параметр severity
может лежать в приделах от 0 до 3. Вероятно, он перепутал это со способом формирования кодов ошибки, используемом при работе с функциями GetLastError()
/ SetLastError()
.
Макрос MAKE_HRESULT
в качестве первого аргумента может принимать только 0 (success) или 1 (failure). Подробнее этот вопрос рассматривался на форуме сайта CodeGuru: Warning! MAKE_HRESULT macro doesn't work.
Так как в качестве первого фактического аргумента используется число 3, то возникает переполнение. Число 3 "превратится" в 1. Из-за этой случайности ошибка не повлияет на работу программы. Однако приведённый пример показывает, что нередко код работает исключительно благодаря везению, а не потому, что написан правильно.
Правильный код:
*hrCode = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, messageID);
Данная диагностика классифицируется как:
|