Информация об изменениях

Сообщение Re[10]: Meltdown and Spectre от 07.01.2018 14:06

Изменено 07.01.2018 14:26 netch80

Re[10]: Meltdown and Spectre
Здравствуйте, ononim, Вы писали:

O>>>инкремент volatile int переменной через __sync_add_and_fetch:

C>>Ну хорошо, пусть будет явный барьер в виде инструкции. Смысл не меняется.
O>Меняется. Эта инструкция форсит синхронизацию кэша и памяти. То есть ее исполнение в бэкграунде просто ломает атаку, которая основана на том что время доступа к кэшу != время доступа к памяти.

Нет. По крайней мере на x86 всё, чего тут достигается, это, в зависимости от факта владения изменённой строкой до операции, и от конкретного диалекта MOESI-like протокола, это или
1) превращение владения данной строкой кэша в единоличное (modified, но при чтении — вначале через exclusive), копии у остальных участников данного домена памяти дискардятся,
или
2) оповещение остальных участников шины про изменение строки кэша — банальное идущее по спецшине сообщение "данные по адресу X изменились" (но при этом предварительно требуется забрать себе owned — владение изменением строки, что проще делать через то же самое единоличное владение).
Собственно _запись в RAM_ при этом не требуется, и в подавляющем большинстве случаев не произойдёт, строка останется в L1 (на сотни-тысячи тактов вперёд).
Задержки же будут вызваны необходимостью синхронизации владения (если ядро B начало такую операцию, пока ядро A владеет модификацией строки, оно вначале должно договориться о добровольной отдаче ядром A такого владения).

При уже состоявшемся владении и отсутствия попыток перезаписи со стороны — другие не увидят никакой разницы между раздельным чтением и записью, и всеми вариантами CAS, xadd и т.п. вместе взятыми — просто им могут прийти (а могут и не прийти, если строка в чистом exclusive/modified) оповещения об изменении.
Разница начнётся только тогда, когда другие будут пытаться забрать право модификации строки себе. Вот тогда разница в том, что запись при раздельном чтении и записи — отдаст строку, а потом долго (десятки тактов) будет пытаться забрать её себе обратно, а при locked CAS (cmpxchg), xadd и т.п. — просто не отдаст до конца операции.

Описываемый вариант с чтением из другой нити с другого ядра приведёт к тому, что
1) первое чтение ядром B сменит статус строки на ядре A с exclusive (дёшево работать, потому что никого не надо вообще дёргать по её поводу) или shared (уже надо предупреждать) на owned (у других есть копия на чтение, и у A изменённая); на B она появится в shared;
2) каждое изменение в A (в описываемой схеме — инкремент счётчика) вызовет бродкаст от A "строка изменилась"; оно может содержать изменённую версию строки, а может и не содержать (тогда B забудет эту строку и будет потом читать заново прямым запросом "кто текущий владелец, дайте содержимое").

Это всё может происходить и быстро, и медленно, в зависимости от таймингов/скоростей и уровня загрузки межпроцессорного интерконнекта, но эти времена никак напрямую не связаны с временами доступа к RAM. Типично на это тратятся времена порядка десятков тактов, сравнимо со временем доступа в L3 и на порядок быстрее доступа в RAM.

Измерить некоторое время, которое или от 0 до X, или уже порядка 10*X, с помощью инструмента, дающего абсолютную погрешность X в большинстве случаев и, вероятно, до 3*X в статистически малой доле случаев — таки можно.

C>>Я могу повторять пробу много раз, так что статистически даже намного менее точный таймер будет достаточен.

O>Ты можешь повторять пробу бесконечное количество раз, но если результат операций меньше разрешения таймера — усреднив свою статистику ты просто получишь значение разрешния таймера и не более того.
O>Давай более приближенный к жизни пример. Вот у тебя есть команда ping, и так повелось, что она умеет выдавать результат с точностью до 10 мсек. Как ты с нее помощью определишь какой из двух хостов к тебе ближе, учитывая что round-trip-time до одного из них — 1мсек, до другого — 2мсек?

В твоей аналогии на самом деле RTT оказывается в существенной доле случаев порядка 100 мс, и это именно в тех случаях, когда на сети что-то произошло важное для измеряющего. И да, пинг это покажет, даже если в 1/10 случаев он получит данные слишком поздно.

O>Демагогия.. КРоме того атака на все остальное опять же требует точного измерения времени.


Его точность достаточна.
Re[10]: Meltdown and Spectre
Здравствуйте, ononim, Вы писали:

O>>>инкремент volatile int переменной через __sync_add_and_fetch:

C>>Ну хорошо, пусть будет явный барьер в виде инструкции. Смысл не меняется.
O>Меняется. Эта инструкция форсит синхронизацию кэша и памяти. То есть ее исполнение в бэкграунде просто ломает атаку, которая основана на том что время доступа к кэшу != время доступа к памяти.

Нет. По крайней мере на x86 всё, чего тут достигается, это, в зависимости от факта владения изменённой строкой до операции, и от конкретного диалекта MOESI-like протокола, это или
1) превращение владения данной строкой кэша в единоличное (modified, но при чтении — вначале через exclusive), копии у остальных участников данного домена памяти дискардятся,
или
2) оповещение остальных участников шины про изменение строки кэша — банальное идущее по спецшине сообщение "данные по адресу X изменились" (но при этом предварительно требуется забрать себе owned — владение изменением строки, что проще делать через то же самое единоличное владение).
Собственно _запись в RAM_ при этом не требуется, и в подавляющем большинстве случаев не произойдёт, строка останется в L1 (на сотни-тысячи тактов вперёд).
Задержки же будут вызваны необходимостью синхронизации владения (если ядро B начало такую операцию, пока ядро A владеет модификацией строки, оно вначале должно договориться о добровольной отдаче ядром A такого владения).

При уже состоявшемся владении и отсутствия попыток перезаписи со стороны — другие не увидят никакой разницы между раздельным чтением и записью, и всеми вариантами CAS, xadd и т.п. вместе взятыми — просто им могут прийти (а могут и не прийти, если строка в чистом exclusive/modified) оповещения об изменении.
Разница начнётся только тогда, когда другие будут пытаться забрать право модификации строки себе. Вот тогда разница в том, что запись при раздельном чтении и записи — отдаст строку, а потом долго (десятки тактов) будет пытаться забрать её себе обратно, а при locked CAS (cmpxchg), xadd и т.п. — просто не отдаст до конца операции.

Описываемый вариант с чтением из другой нити с другого ядра приведёт к тому, что
1) первое чтение ядром B сменит статус строки на ядре A с exclusive (дёшево работать, потому что никого не надо вообще дёргать по её поводу) или shared (уже надо предупреждать) на owned (у других есть копия на чтение, и у A изменённая); на B она появится в shared;
2) каждое изменение в A (в описываемой схеме — инкремент счётчика) вызовет бродкаст от A "строка изменилась"; оно может содержать изменённую версию строки, а может и не содержать (тогда B забудет эту строку и будет потом читать заново прямым запросом "кто текущий владелец, дайте содержимое").

Это всё может происходить и быстро, и медленно, в зависимости от таймингов/скоростей и уровня загрузки межпроцессорного интерконнекта, но эти времена никак напрямую не связаны с временами доступа к RAM. Типично на это тратятся времена порядка десятков тактов, немного больше времени доступа в L3 и на порядок быстрее доступа в RAM (хотя и на порядок больше, чем если бы операция выполнялась вообще без синхронизации, просто на кэше).

Измерить некоторое время, которое или от 0 до X, или уже порядка 10*X, с помощью инструмента, дающего абсолютную погрешность X в большинстве случаев и, вероятно, до 3*X в статистически малой доле случаев — таки можно. Измерить время инструментом, погрешность которого сравнима с измеряемым значением и достаточно случайно прыгает — тоже можно, но на множестве проб.

C>>Я могу повторять пробу много раз, так что статистически даже намного менее точный таймер будет достаточен.

O>Ты можешь повторять пробу бесконечное количество раз, но если результат операций меньше разрешения таймера — усреднив свою статистику ты просто получишь значение разрешния таймера и не более того.
O>Давай более приближенный к жизни пример. Вот у тебя есть команда ping, и так повелось, что она умеет выдавать результат с точностью до 10 мсек. Как ты с нее помощью определишь какой из двух хостов к тебе ближе, учитывая что round-trip-time до одного из них — 1мсек, до другого — 2мсек?

В твоей аналогии на самом деле RTT оказывается в существенной доле случаев порядка 100 мс, и это именно в тех случаях, когда на сети что-то произошло важное для измеряющего. И да, пинг это покажет, даже если в 1/10 случаев он получит данные слишком поздно.

O>Демагогия.. КРоме того атака на все остальное опять же требует точного измерения времени.


Его точность достаточна.