Здравствуйте, 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>Демагогия.. КРоме того атака на все остальное опять же требует точного измерения времени.
Здравствуйте, wildwind, Вы писали:
W>В результате speculative execution область памяти ядра загружается в кэш. Оттуда его можно достать с помощью техники, аналогичной Spectre.
Т.е. я правильно понимаю, что спекулятивные инструкции таки имеют side-effect'ы в данном случае? Я то думал что из-за длины конвейера(у интела сильно больше 20 шагов) оне не дойдут до непосредственного исполнения.
V>Таким образом, объектом атаки является микроархитектура процессора, и саму атаку в софте не починить.
Вот это не понял... Можно же после ветвлений на нужное кол-во тактов(сколько нужно пока условие перехода не выч.) nop'ы расставлять. И разумеется это может делать компилятор. И разумеется производительность дико просядет.
Здравствуйте, kov_serg, Вы писали:
_>Не красиво. Есть же виртуальная память и у каждого процесса она своя. Что мешает не иметь охраняемых данных в ввиртуальном адресном пространстве вообще?
Именно что красиво. И просто. Каждый процесс имеет копию памяти ядра. Надо принципиально арх-ру ОСей современных менять.
Здравствуйте, netch80, Вы писали:
N>Здравствуйте, kov_serg, Вы писали:
N>То есть сделать правильно было можно. Не захотели.
По науке, возможно. Но софт, а главное ОСи, написаны под уже сущ. реализацию механизма ВП. Не думаю, что миграция была бы безболезненной.
N>2. Права доступа к странице проверяются только при "реализации" действия команды, но не при предвыборке с предисполнением (результаты которого ещё не фиксируются).
N>А вот это уже совсем кретинизм, которому исполнилось более 20 лет.
Правильно ли я понимаю (после n-ого спекулятивного комментария в этой ветке), что вся соль уязвимости в том, что процессор "выполняет" (на уровне подгрузки данных в кэш) спекулятивную инструкцию? Если так, то ведь наверняка об этом давно знали, ведь на поверхности же.
Здравствуйте, Cyberax, Вы писали:
C> Видимо, Intel сэкономили на битах защиты для кэша первого уровня, в отличие от AMD. Так что для них это получилось намного дороже.
Т.е. то, что приташили из kernel-mode нельзя прочитать в user-mode?
Здравствуйте, Sharov, Вы писали:
S>Здравствуйте, Vain, Вы писали:
V>>
V>>Таким образом, объектом атаки является микроархитектура процессора, и саму атаку в софте не починить.
S>Вот это не понял... Можно же после ветвлений на нужное кол-во тактов(сколько нужно пока условие перехода не выч.) nop'ы расставлять. И разумеется это может делать компилятор. И разумеется производительность дико просядет.
дак хакер в своём эксплойте этого делать не будет.
Здравствуйте, wildwind, Вы писали:
W>В плане влияния на результаты вычислений (регистры и память) нет, не имеют. Все откатывается, но кэш остается.
Да, это я и имел в виду. Я думал, что спекулятивная инструкция до фазы маломальски активного исполнения не дойдет. Но оптимизация на оптимизации дают о себе знать.
Здравствуйте, ononim, Вы писали:
O>side-channel через канал времени лучше всего убить убив этот самый канал, то есть — загрубив потенциально возможные результаты измерения времени. То что я выше предложил — только один способ. Второй может быть например "дрыгать" частоту ядер рандомом. Тут в принципе ничего особенного нету — динамическим изменением частоты никого не удивишь нынче, речь о том чтоб ее дрыгать _всегда_ и часто в целях секурности. Вплоть до рандомизации скважности тактового генератора. Решения, типа понаставлять везде в ядре LFENCE — это просто затычки в решете.
Начать с того, что по-моему проблемы здесь вообще нет. Она возникла только из-за того, что авторы некоторых программ, в частности JS в браузерах, решили, что могут сделать песочницу для кода в пределах одного с ним адресного пространства. Если бы они отдавали исполнение в другой процесс никакой уязвимости от Spectre не возникло бы. Из-за этого решения какие-то уязвимости и протечки и без обсуждаемых багов постоянно находятся. Meltdown — другое дело, это уже именно баг в куче интеловских процессоров.
Здравствуйте, Sharov, Вы писали:
V>>Таким образом, объектом атаки является микроархитектура процессора, и саму атаку в софте не починить. S>Вот это не понял... Можно же после ветвлений на нужное кол-во тактов(сколько нужно пока условие перехода не выч.) nop'ы расставлять. И разумеется это может делать компилятор. И разумеется производительность дико просядет.
Спекулятивно может выполняться до нескольких сотен инструкций. Но идея похоже на то, что делает Intel — добавляет инструкции для явного сброса предсказателя в следующем выпуске микрокода.
Здравствуйте, Sharov, Вы писали:
C>> Видимо, Intel сэкономили на битах защиты для кэша первого уровня, в отличие от AMD. Так что для них это получилось намного дороже. S>Т.е. то, что приташили из kernel-mode нельзя прочитать в user-mode?
В AMD нельзя, так как в L1 лежат биты защиты, так что их проверка получается дешёвой и синхронной.
Здравствуйте, Cyberax, Вы писали:
C>Спекулятивно может выполняться до нескольких сотен инструкций. Но идея похоже на то, что делает Intel — добавляет инструкции для явного сброса предсказателя в следующем выпуске микрокода.
У меня само предположение глупо -- чинить в компиляторе это не нужно, поскольку аттакующий все равно подчистит код.
Здравствуйте, andrey.desman, Вы писали:
Pzz>>Примерно понятно. Интересно, а с какой скоростью таким методом можно данные вычитывать?
AD>В зависимости от ОС и метода от 122 до ~500 КБ/с.
А насколько нужно загрузить процессор, чтобы добывать данные из ядра с приемлемой скоростью, и при этом работать незаметно для пользователя обычной рабочей станции?
Здравствуйте, ononim, Вы писали:
O>Думаю такие busy-loopы можно легко детектить шедулером, и, продетектив, — начать им подгаживать.
Еще правильнее будет предпринимать какие-нибудь действия (сброс кэша, организацию задержки и т.п.) при любом страничном отказе, не влекущем за собой подкачки страницы. Поскольку такой отказ — всегда аварийная ситуация, трудно представить себе код, генерирующий плотный поток таких отказов сугубо в своих интересах. На ум приходит только что-то вроде самопальной реализации виртуальной памяти для обработки чего-нибудь типа разреженных множеств, но для таких специфических задач можно придумать обход.
Здравствуйте, ononim, Вы писали:
O>Предлагаю элегантное сцуко-решение. Выпилить возможность точного измерениея времени исполнения кода из юзермода путем запрета RDTSD в CR4. Как минимум сделать настраиваемым — кому нужно (всякие там эти.. программисты) — включат. Остальным — софтово эмулировать с дикой погрешностью.
И это сразу ударит по коду, который реализует чувствительные ко времени сетевые протоколы в user space.
Здравствуйте, ononim, Вы писали:
O>Думаю такие busy-loopы можно легко детектить шедулером, и, продетектив, — начать им подгаживать. Впрочем, раз "обсуждается", то это наверно тоже уже обсуждалось.
А чем, с точки зрения планировщика, такой busy-loop отличается от кода, который майнит биткоин на процессоре?
Здравствуйте, Sharov, Вы писали:
C>>Спекулятивно может выполняться до нескольких сотен инструкций. Но идея похоже на то, что делает Intel — добавляет инструкции для явного сброса предсказателя в следующем выпуске микрокода. S>У меня само предположение глупо -- чинить в компиляторе это не нужно, поскольку аттакующий все равно подчистит код.
Для Spectre у атакующего нет контроля над взламываемым кодом.