Почему этот код ломается, когда включен -O2 или выше? - PullRequest
6 голосов
/ 08 марта 2019

Я попытался разместить реализацию NSA SPECK в 8-битном микроконтроллере PIC. В бесплатной версии их компилятора (на основе CLANG) не будет разрешена оптимизация, поэтому мне не хватило памяти. Я попробовал "пробную" версию, которая включает -O2, -O3 и -Os (оптимизировать по размеру) С помощью -Os удалось разместить мой код в памяти программной области 2K.

Вот код:

#include <stdint.h>
#include <string.h>

#define ROR(x, r) ((x >> r) | (x << (32 - r)))
#define ROL(x, r) ((x << r) | (x >> (32 - r)))
#define R(x, y, k) (x = ROR(x, 8), x += y, x ^= k, y = ROL(y, 3), y ^= x)
#define ROUNDS 27

void encrypt_block(uint32_t ct[2],
        uint32_t const pt[2],
        uint32_t const K[4]) {
    uint32_t x = pt[0], y = pt[1];
    uint32_t a = K[0], b = K[1], c = K[2], d = K[3];

    R(y, x, a);
    for (int i = 0; i < ROUNDS - 3; i += 3) {
        R(b, a, i);
        R(y, x, a);
        R(c, a, i + 1);
        R(y, x, a);
        R(d, a, i + 2);
        R(y, x, a);
    }
    R(b, a, ROUNDS - 3);
    R(y, x, a);
    R(c, a, ROUNDS - 2);
    R(y, x, a);

    ct[0] = x;
    ct[1] = y;
}

К сожалению, при отладке его построчно, сравнивая его с тестовыми векторами в руководстве по реализации со стр. 32, "15 тестовых векторов SPECK64 / 128", результаты отличаются от ожидаемых результатов.

Вот способ вызова этой функции:

uint32_t out[2];
uint32_t in[] = { 0x7475432d, 0x3b726574 };
uint32_t key[] = { 0x3020100, 0xb0a0908, 0x13121110, 0x1b1a1918 };

encrypt_block(out, in, key);

assert(out[0] == 0x454e028b);
assert(out[1] == 0x8c6fa548);

Ожидаемое значение для «out», согласно руководству, должно составлять 0x454e028b, 0x8c6fa548. Результат, который я получаю с -O2, равен 0x8FA3FED7 0x53D8CEA8. С -O1 я получаю 0x454e028b, 0x8c6fa548, что является правильным результатом.

пошаговая отладка

Руководство по внедрению включает в себя все значения промежуточного ключевого расписания и другие значения, поэтому я пошагово прошел по коду кода, сравнивая результаты с руководством.

Ожидаемые результаты для «x»: 03020100, 131d0309, bbd80d53, 0d334df3. Я начинаю пошаговую отладку, но при достижении 4-го результата, 0d334df3, в окне отладчика отображается 0d334df0. К следующему раунду ожидаемое значение 7fa43565 равно 7FA43578 и ухудшается с каждой итерацией.

Это происходит только при включенном -O2 или выше. Без оптимизации или с параметром -O1 код работает должным образом.

Ответы [ 3 ]

3 голосов
/ 11 марта 2019

Это была ошибка в компиляторе.

Я разместил вопрос на форуме производителя . Другие люди действительно воспроизвели проблему, которая возникает при компиляции определенных частей. Другие части не затрагиваются.

В качестве обходного пути я изменил макросы на реальные функции и разделил операцию на две строки:

uint32_t ROL(uint32_t x, uint8_t r) {
    uint32_t intermedio;
    intermedio = x << r;
    intermedio |= x >> (32 - r);
    return intermedio;
}

Это дает правильный результат.

2 голосов
/ 08 марта 2019

Публикация скомпилированного тестового кода в качестве ссылки.

#include <stdint.h>
#include <string.h>
//#include "speck.h"

#define ROR(x, r) ((x >> r) | (x << (32 - r)))
#define ROL(x, r) ((x << r) | (x >> (32 - r)))
#define R(x, y, k) (x = ROR(x, 8), x += y, x ^= k, y = ROL(y, 3), y ^= x)
#define ROUNDS 27

void encrypt_block(uint32_t ct[2], uint32_t const pt[2], uint32_t const K[4]) {
  uint32_t x = pt[0], y = pt[1];
  uint32_t a = K[0], b = K[1], c = K[2], d = K[3];

  R(y, x, a);
  // for (int i = 0; i < ROUNDS - 3; i += 3) {
  for (uint32_t i = 0; i < ROUNDS - 3; i += 3) {
    R(b, a, i);
    R(y, x, a);
    R(c, a, i + 1);
    R(y, x, a);
    R(d, a, i + 2);
    R(y, x, a);
  }
  R(b, a, ROUNDS - 3);
  R(y, x, a);
  R(c, a, ROUNDS - 2);
  R(y, x, a);

  ct[0] = x;
  ct[1] = y;
}

int main(void) {
  uint32_t out[2];
  uint32_t in[] = {0x7475432d, 0x3b726574};
  uint32_t key[] = {0x03020100, 0x0b0a0908, 0x13121110, 0x1b1a1918};
  encrypt_block(out, in, key);

  printf("%8lx %8lx\n", (unsigned long) out[0], 0x454e028bLU);
  printf("%8lx %8lx\n", (unsigned long) out[1], 0x8c6fa548LU);
}

Вывод

454e028b 454e028b
8c6fa548 8c6fa548

Неожиданный вывод

0x8FA3FED7
0x53D8CEA8
1 голос
/ 09 марта 2019

Я не вижу никаких указаний на неопределенное поведение в вашем коде, если только это не относится к какому-то аспекту настройки / точки вызова, который вы не показали.Таким образом, поведение должно отличаться в зависимости от уровня оптимизации.Обычно я бы не стал быстро винить в ошибках компилятора что-то вроде этого, но разветвил компиляторы FOSS для встроенных вещей, и особенно форков, которые переопределяют int в 16-битный компилятор, который не был предназначен для 16-битных int, и особенно проприетарные вилки, где их код настолько плох, что они даже не хотят, чтобы вы его видели, ошибка компилятора очень вероятна.

...