Я пытаюсь понять проблему, которую мы недавно устранили при использовании Clang 5.0 и Undefined Behavior Sanitizer (UBsan). У нас есть код, который обрабатывает буфер в прямом или обратном направлении. Сокращенный регистр похож на код, показанный ниже .
0-len
может выглядеть немного необычно, но он необходим для ранних компиляторов Microsoft .Net. Clang 5.0 и UBsan выявили целочисленное переполнение:
adv-simd.h:1138:26: runtime error: addition of unsigned offset to 0x000003f78cf0 overflowed to 0x000003f78ce0
adv-simd.h:1140:26: runtime error: addition of unsigned offset to 0x000003f78ce0 overflowed to 0x000003f78cd0
adv-simd.h:1142:26: runtime error: addition of unsigned offset to 0x000003f78cd0 overflowed to 0x000003f78cc0
...
Строки 1138, 1140, 1142 (и другие) являются приращением, которое может отставать назад из-за 0-len
.
ptr += inc;
Согласно сравнению указателей в C. Они подписаны или не подписаны? (в котором также обсуждается C++), указатели не являются ни подписанными, ни беззнаковыми. . Наши смещения были беззнаковыми, и мы полагались на перенос целочисленных значений без знака для достижения обратного шага.
Код был в порядке с GCC UBsan и Clang 4 и более ранними версиями UBsan. В конце концов мы разрешили его для Clang 5.0 с помощью разработчиков LLVM< /а>. Вместо size_t
нам нужно было использовать ptrdiff_t
.
Мой вопрос: где было целочисленное переполнение/неопределенное поведение в конструкции? Как ptr + <unsigned>
привело к переполнению целого числа со знаком и к неопределенному поведению?
Вот MSVC, который отражает реальный код.
#include <cstddef>
#include <cstdint>
using namespace std;
uint8_t buffer[64];
int main(int argc, char* argv[])
{
uint8_t * ptr = buffer;
size_t len = sizeof(buffer);
size_t inc = 16;
// This sets up processing the buffer in reverse.
// A flag controls it in the real code.
if (argc%2 == 1)
{
ptr += len - inc;
inc = 0-inc;
}
while (len > 16)
{
// process blocks
ptr += inc;
len -= 16;
}
return 0;
}