Russian Qt Forum

Программирование => С/C++ => Тема начата: Авварон от Май 27, 2019, 19:12



Название: Тупой вопрос про выравнивание
Отправлено: Авварон от Май 27, 2019, 19:12
Вот есть такой код:
Код:
#define AES_N_BLOCK 16

void copy_and_key(quint8 d[AES_N_BLOCK], const quint8 s[AES_N_BLOCK], const quint8 k[AES_N_BLOCK])
{
    ((quint64*)d)[0] = ((quint64*)s)[0] ^ ((quint64*)k)[0];
    ((quint64*)d)[1] = ((quint64*)s)[1] ^ ((quint64*)k)[1];
}

Юзается так:
Код:
void aes_decrypt(const quint8 in[AES_N_BLOCK], quint8 out[AES_N_BLOCK]) {
    quint8 s1[AES_N_BLOCK], r;
    copy_and_key(s1, in, keySchedule + rnd * AES_N_BLOCK);
    // тут что-то делаем c s1
}

Анализатор кланга ругается что одна из ячеек s1 не иницилизирована.
Если я убираю reinterpret_cast из quint8 в quint64 и xor'ю в цикле, то ругаться перестает.
Это бага анализатора? Или это связано с выравниванием - что никто не гарантирует выравнивание quint8 s1[16] на 8 байт? Или гарантируют? На x86_64 добиться левого выравнивания на стеке\маллоком не удалось...

В код можно потупить тут (http://libbtc.github.io/libbtc/html/aes_8c_source.html), у нас копия, но почти 1в1, судя по всему. Функции aes_cbc_decrypt, aes_decrypt, copy_and_key


Название: Re: Тупой вопрос про выравнивание
Отправлено: Igors от Май 28, 2019, 11:43
Объявите union'ом, для удобства присваивания quint64 первым


Название: Re: Тупой вопрос про выравнивание
Отправлено: Авварон от Май 28, 2019, 11:52
Объявите union'ом, для удобства присваивания quint64 первым

Это такое же UB как и код выше. Нельзя использовать union для type prunning'а.
Единственный способ делать type prunning - это memcopy, и да, это тоже помогает "заткнуть" анализатор.
Но всё же хочется воспроизвести проблему, я что не пытаюсь сделать - адреса выровнены, хоть убей. Или это очередная "фишка" х86?


Название: Re: Тупой вопрос про выравнивание
Отправлено: Igors от Май 28, 2019, 12:46
Если нужен только xor то его проще реализовать не привлекая quint64. Насколько понял, Вас волнует что при "наложении" этой структуры на какую-то область памяти адрес quint64 окажется не кратным 8. Не вижу почему это не должно работать. Напр
Код
C++ (Qt)
#pragma pack(push, 1)
struct Data {
char ch;
quint64 val;
};
#pragma pack(pop)
Так делать не запрещено, нечетный адрес val замедлит выполнение, но и только

Ну или рисовать геттеры/сеттеры c memmоve внутри (чтоб сердце успокоилось)


Название: Re: Тупой вопрос про выравнивание
Отправлено: Авварон от Май 28, 2019, 16:12
Так делать не запрещено, нечетный адрес val замедлит выполнение, но и только
Я в этом не уверен :)

Цитировать
Taking the address of a field in a #pragma packed struct does not yield a __packed pointer, so the compiler will not produce an error if you assign this address to a non-__packed pointer. However, the field might not be properly aligned for its type, and dereferencing such an unaligned pointer results in Undefined behavior.

отсюда (http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0491g/CJAFEEDG.html)

Но мы отвлеклись, вопрос не в том, можно ли reinterpret_cast'ить невровненные адреса. Понятно, что нельзя.
Вопрос-то про конкретный код, где мы передаем в функцию uint8[16], созданный на стеке. Он будет выровнен или нет? Мои знания говорят, что нет, но мои эксперименты на x86 этого не подтверждают - у меня не получилось создать невыровненный массив на стеке.


Название: Re: Тупой вопрос про выравнивание
Отправлено: ViTech от Май 28, 2019, 17:42
Вопрос-то про конкретный код, где мы передаем в функцию uint8[16], созданный на стеке. Он будет выровнен или нет? Мои знания говорят, что нет, но мои эксперименты на x86 этого не подтверждают - у меня не получилось создать невыровненный массив на стеке.

Про выравнивание мало что могу точно сказать, нужно букварь больше читать. Посмотрите, как располагаются в памяти массивы с другим размером, например uint8[15]. У меня в отладчике такой не выглядит выровненным. Может конкретно uint8[16] компилятор скорей всего выровняет (и выравнивает), но не обязан это делать, на что clang и обращает внимание.

alignas (https://en.cppreference.com/w/cpp/language/alignas) в данной ситуации может помочь?


Название: Re: Тупой вопрос про выравнивание
Отправлено: Old от Май 28, 2019, 21:45
Это бага анализатора?
Думается мне, что это именно анализатор тупит.

Или это связано с выравниванием - что никто не гарантирует выравнивание quint8 s1[16] на 8 байт?
А для чего здесь иметь обязательно выравненное значение? x86_64 может читать/писать не выравненные данные.

На x86_64 добиться левого выравнивания на стеке\маллоком не удалось...
Попробуйте так:
quint8 s1[AES_N_BLOCK+1];

плюс этот массив можно инициализировать какими-то значениями и после отработки функции посмотреть все ли в нем изменилось.
 
Update. А вот на некоторых армовских ядрах могут быть проблемы с этим. Возможно анализатор заточен на некий  общий случай.



Название: Re: Тупой вопрос про выравнивание
Отправлено: _Bers от Июнь 01, 2019, 13:39
Это бага анализатора?

код содержит UB.
1.
нарушение стрикт-алиасинга.

2.
нет гарантий для выравнивания.



Название: Re: Тупой вопрос про выравнивание
Отправлено: Авварон от Июнь 01, 2019, 16:26
Это бага анализатора?

код содержит UB.
1.
нарушение стрикт-алиасинга.

2.
нет гарантий для выравнивания.



1. Верно я понимаю, что исключения для стрикт алиссинга распространяется только на void*,char*,uchar*,std::byte*?

2. Так поможет?:
Код:
struct alignas(alignof(uint64_t)) AesBlock { uchar8_t data[16]; }

Если я буду передавать const-ref структуры и возвращать копию структуры будет ок?


Название: Re: Тупой вопрос про выравнивание
Отправлено: Igors от Июнь 02, 2019, 07:47
Гарантируют - не гарантируют... Хочется чтоб "никто не подкопался" - ну просто так
Код
C++ (Qt)
namespace ns_Dummy {
 
 quint64 Get( const quint8 * addr, size_t index )
 {
   quint64 val;
   memmove(&val, addr + index * sizeof(quint64), sizeof(quin64));
   return val;
 }
 
 void Set( quint8 * addr, size_t index, quint64 val )
 {
   memmove(addr + index * sizeof(quint64), &val, sizeof(quin64));
 }
 
void copy_and_key( quint8 * dst, const quint8 * src1, quint8 * src2 )
{
   Set(dst, 0, Get(src1, 0) ^ Get(src2, 0));
   Set(dst, 1, Get(src1, 1) ^ Get(src2, 1));
}
 
}  // namespace
 


Название: Re: Тупой вопрос про выравнивание
Отправлено: Авварон от Июнь 02, 2019, 12:15
Ну способ с memcpy понятен, только зачем, можно просто побайтовом покорить - будет быстрее, скорее всего.
Так-то велосипед писать не надо, всё уже написано за вас (https://github.com/qt/qtbase/blob/5.12/src/corelib/global/qendian.h#L64).


Название: Re: Тупой вопрос про выравнивание
Отправлено: Igors от Июнь 02, 2019, 13:21
Ну способ с memcpy понятен,
Если понятен - то надо делать и забывать  :)
всё уже написано за вас (https://github.com/qt/qtbase/blob/5.12/src/corelib/global/qendian.h#L64).
Я давно заметил что "богатырская сила" охотно демонстрируется когда и без нее  проблем нет. А вот когда трудности и помощь была бы очень кстати - не дождесся  :'(