Подтвердить что ты не робот

Случается ли спекуляция о зависимости от памяти, чтобы BN_consttime_swap не был постоянным?

Контекст

Функция BN_consttime_swap в OpenSSL - вещь прекрасная. В этом фрагменте condition был вычислен как 0 или (BN_ULONG)-1:

#define BN_CONSTTIME_SWAP(ind) \
    do { \
            t = (a->d[ind] ^ b->d[ind]) & condition; \
            a->d[ind] ^= t; \
            b->d[ind] ^= t; \
    } while (0)
…
    BN_CONSTTIME_SWAP(9);
…
    BN_CONSTTIME_SWAP(8);
…
    BN_CONSTTIME_SWAP(7);

Цель состоит в том, чтобы гарантировать, что операции bignum более высокого уровня занимают постоянное время, эта функция либо заменяет два бонуса, либо оставляет их на месте в постоянное время. Когда он оставляет их на месте, он на самом деле читает каждое слово каждого бонуса, вычисляет новое слово, которое идентично старому слову, и записывает этот результат обратно в исходное местоположение.

Цель состоит в том, что это займет то же самое время, как если бы бонусы были эффективно заменены.

В этом вопросе я предполагаю современную, широко распространенную архитектуру, такую ​​как те, которые описаны Agner Fog в его руководствах по оптимизации. Предполагается также простой перевод кода C на сборку (без компилятора C, отменяющего усилия программиста).

Вопрос

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

В частности, меня беспокоит сценарий, когда bignum a уже находится в кэше данных L1 при вызове функции BN_consttime_swap, а код сразу после возвращения функции начинает работать с правами bignum a далеко. На современном процессоре достаточно инструкций может быть одновременно в полете, чтобы копия не была технически закончена, когда используется bignum a. Механизм, позволяющий инструкциям после вызова BN_consttime_swap работать на a, - это спекуляция о зависимости от памяти. Предположим наивные предположения о зависимости от памяти для аргумента.

Кажется, что этот вопрос сводится к следующему:

Когда процессор, наконец, обнаруживает, что код после BN_consttime_swap читается из памяти, который, вопреки спекуляции, был написан внутри функции, отменяет ли он спекулятивное исполнение, как только он обнаруживает, что адрес написано или позволяет сохранить его, когда обнаруживает, что значение, которое было написано, совпадает с значением, которое уже было там?

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

Даже во втором случае это похоже на то, что он может быть исправлен в обозримом будущем (пока процессоры остаются наивными) за каждое слово каждого из двух бонусов, записывая значение, отличное от два возможных окончательных значения перед записью либо старого значения, либо нового значения. Квалификатор типа volatile, возможно, должен быть задействован в какой-то момент, чтобы не позволить обычным компиляторам чрезмерно оптимизировать последовательность, но это все еще кажется возможным.

ПРИМЕЧАНИЕ. Я знаю о сохранении пересылки, но сохранение пересылки - это только ярлык. Это не мешает чтению, выполняемому до, писать, которое оно должно последовать. И в некоторых случаях это терпит неудачу, хотя в этом случае этого не ожидалось.

4b9b3361

Ответ 1

Это сообщение в блоге и комментарии, сделанные автор, Генри, по этому вопросу должен считаться авторитетным, как и следовало ожидать. Я воспроизведу последний здесь для архива:

Я не думал, что случай перезаписи места памяти с тем же значением имел практическое применение. Я думаю, что ответ заключается в том, что в текущих процессорах значение хранилища не имеет значения, важно только адрес.

Здесь, в академических кругах, Ive слышал о двух подходах к устранению неоднозначности памяти: на основе адресов или на основе значений. Насколько я знаю, текущие процессоры все имеют адресное значение.

Я думаю, что в текущем микробенчмарке есть некоторые доказательства того, что значение не имеет значения. Многие из случаев включают в себя повторное хранение одного и того же значения в одном и том же месте (в частности, со смещением = 0). Это не было ненормально быстро.

Схемы, основанные на адресах, используют очередь хранилища и очередь загрузки для отслеживания операций с памятью. Loads проверяет очередь магазина на соответствие адреса (если эта загрузка делает пересылку хранения в загрузку вместо чтения из кеша?), В то время как магазины проверяют очередь загрузки (это хранилище блокирует местоположение более поздней загрузки, которую я разрешил выполнить рано?). Эти проверки полностью основаны на адресах (когда хранилище и загрузка столкнулись). Одно из преимуществ этой схемы заключается в том, что ее довольно простое расширение поверх пересылки между загрузкой и загрузкой, так как там также используется поиск в очереди на хранение.

Ценностные схемы избавляются от ассоциативного поиска (т.е. быстрее, снижают мощность и т.д.), но требуют лучшего прогноза для пересылки между хранилищем и загрузкой (теперь вам нужно угадать, вместо поиска SQ). Эти схемы проверяют порядок нарушений (и неправильную переадресацию) путем повторной загрузки нагрузок в момент фиксации и проверки правильности их значений. В этих схемах, если у вас есть конфликтующее хранилище (или сделано некоторая другая ошибка), которое по-прежнему привело к правильному значению результата, оно не будет обнаружено как нарушение порядка.

Могут ли будущие процессоры перейти к схемам, основанным на стоимости? Я подозреваю, что они могут. Они были предложены в середине 2000-х (?), Чтобы уменьшить сложность аппаратного обеспечения памяти.

Ответ 2

Предполагается также простой перенос кода C на сборку (без компилятора C, отменяющего усилия программиста).

Я знаю, что это не суть твоего вопроса, и я знаю, что ты это знаешь, но мне нужно немного на минутку. Это даже не квалифицируется как попытка "лучшего усилия" обеспечить постоянное выполнение. Компилятор имеет лицензию на проверку значения condition и пропускает все, если condition равно нулю. Обфускация установки condition делает это менее вероятным, но не является гарантией.

Обязательно "постоянный" код не должен записываться на C, полная остановка. Даже если это постоянное время сегодня, на компиляторах, которые вы тестируете, умный компилятор придет и победит вас. Один из ваших пользователей будет использовать этот компилятор до того, как вы это сделаете, и они не будут знать о риске, которому вы его разоблачили. Существует ровно три способа достижения постоянного времени, которое я знаю: выделенное оборудование, сборка или DSL, который генерирует машинный код, а также доказательство непрерывного выполнения.


Откажитесь от вопроса о реальном архитектурном вопросе: предполагая глупо наивный компилятор, этот код является постоянным временем на μarches, с которым я достаточно знаком, чтобы оценить этот вопрос, и я ожидаю, что он будет в целом справедливым для одного простой причина: сила. Я ожидаю, что проверка в очереди магазина или кеше, если значение, которое хранится, соответствует уже существующему значению и условно короткому замыканию магазина или избегает загрязнять линию кэша на каждом магазине, потребляет больше энергии, чем было бы спасено в редком случае, чтобы избежать некоторой работы. Тем не менее, я не дизайнер ЦП и не могу позволить себе говорить от их имени, поэтому возьмите это с несколькими столовыми ложками соли и проконсультируйтесь с ним, прежде чем считать, что это правда.

Ответ 3

Идея реализации постоянного времени - это не выполнять все в постоянное время. Это никогда не произойдет в архитектуре вне порядка. Требование состоит в том, что никакая секретная информация не может быть выявлена ​​путем анализа времени. Для предотвращения этого существуют в основном два требования:

a) Не используйте ничего секретного как условие остановки для цикла или как предикат для ветки. В противном случае вы откроете атаку на прогнозирование ветвей https://eprint.iacr.org/2006/351.pdf

b) Не используйте ничего секретного в качестве индекса для доступа к памяти. Это приводит к атакам тайминга тайма http://www.daemonology.net/papers/htt.pdf

Что касается вашего кода: считая, что ваш секрет является "условием" и, возможно, содержимым a и b, код является абсолютно постоянным временем в том смысле, что его выполнение не зависит от фактического содержимого a, b и условия. Конечно, местность a и b в памяти влияет на время выполнения цикла, но не на СОДЕРЖАНИЕ, которые являются секретными. Это предполагает, что, конечно, условие вычислялось в течение постоянного времени. Что касается оптимизаций C: компилятор может только оптимизировать код на основе информации, которую он знает. Если "условие" действительно секретно, компилятор не должен распознавать его содержимое и оптимизировать. Если его можно вычесть из вашего кода, тогда компилятор, скорее всего, сделает оптимизацию для случая 0.