Для обеспечения сохранности содержимого Dynamic RAM (DRAM) его необходимо периодически обновлять. Если значение какого-то бита поменяется самопроизвольно, то можно считать, что память работает не так, как должна. JEDEC (Joint Electron Device Engineering Council — организация, среди прочего, разработавшая спецификацию DDR) определяет, как часто это должно происходить для обеспечения надлежащей работы. Однако некоторых людей больше интересует, как долго данные могут сохраняться после отключения питания DRAM. В этом посте мы расскажем, как это можно измерить, а также поделимся результатами, полученными при исследовании пары протестированных платформ.
Немного теории
RAM (Random-Access Memory, запоминающее устройство с произвольным доступом) — это тип памяти, доступ к которому может выполняться в произвольных локациях почти без различий во времени между разными адресами. В этой статье этот термин будет использоваться для описания памяти единого(*) видимого операционной системе последовательного диапазона адресов.
DRAM (Dynamic RAM) — это тип RAM, хранящий данные в конденсаторах, постепенно теряющих свой заряд и требующих периодического обновления. В этом его отличие от статического RAM (SRAM), не требующего обновления для хранения данных.
Конденсатор и логика, требуемая для доступа к его содержимому, называется ячейкой DRAM. Множество ячеек формирует сетку, адресация в которой выполняется по строкам и столбцам; эта сетка называется банком. Каждый банк (в большинстве стандартных чипов DRAM) параллельно дублируется 4 или 8 раз, то есть каждая пара «строка-столбец» одновременно адресует 4 или 8 битов данных DRAM. Кроме того, каждая DRAM может иметь множество банков, адресуемых по группе банков и адресным битам банка (в DDR4 до 2 битов на каждый).
Простая сетка DRAM с двумя битами строк и двумя битами столбцов
DIMM (Dual In-Line Memory Module) — это модуль, который чаще всего подразумевается под терминами наподобие «модуль RAM» или «планка памяти». Он устанавливается в разъёмы на материнской плате, и его можно легко заменять без каких-либо специальных инструментов. DIMM состоят из чипов DRAM, количество которых кратно 8 (или 9 с ECC), каждый из которых имеют общую адресацию строк, столбцов и банков. Если количество DRAM больше, то они включаются в группы по сигналам выбора чипов; этим управляет DIMM на основании адреса рангов, затребованного платформой. Все линии данных DRAM выстроены параллельно, образуя шину данных DIMM шириной 64 битов (72 бита с ECC).
Отображение непрерывного(*) диапазона адресов системой памяти в многоуровневую адресацию DIMM и DRAM выполняется контроллером памяти платформы. Последовательные системные адреса не обязаны быть последовательными в DRAM. На самом деле, для снижения задержек они часто чередуются (по банкам, группам банков, рангам и даже на уровне DIMM). Обычно производители не публикуют отображение, оно может отличаться в зависимости от версии прошивки, параметров и топологии DIMM. Это означает, что паттерн хранимых данных (потенциально неравномерный) может отличаться для разных платформ, что может мешать целевым атакам, но в среднем скорость затухания заряда хранения данных для разных платформ должна быть более-менее одинаковой.
(*) На практике, особенно на платформах x86, в адресации существует множество «дыр»; они по большей мере используются для совместимости, и их местоположение определяется архитектурой. Память, которая должна быть доступна по этим адресам (часто называемая «украденной памятью», stolen memory), отображается в конец пространства RAM.
Цель этого исследования
Наша главная цель — определить время для потери всех данных из RAM без возможности восстановления после отключения питания платформы.
Для того, чтобы значения данных в DRAM сохранялись, их нужно периодически обновлять. Период операций обновления чётко определён JEDEC. Интервал обновлений называется tREFI. Это значение описывает минимальную частоту обновлений при обычных условиях (температурном диапазоне, тактовой частоте), обеспечивающую надёжную работу памяти. После каждой команды обновления данные из обновляемых ячеек недоступны в течение следующего tRFC (времени цикла обновления). Для повышения производительности в крайне специфичных условиях эту операцию можно отложить или заранее выполнить множество обновлений, чтобы последующие отправлялись позже, однако средний интервал не должен превосходить tREFI. Для реального интервала можно установить значение меньше tREFI, например, когда DRAM работает при повышенных температурах или для повышенной устойчивости к атакам наподобие RowHammer.
Для DDR4 базовое значение tREFI равно 7,8 мкс. Исследованиями было определено, что это максимальное значение, обеспечивающее надёжную работу при стандартных условиях температурного диапазона и частот, то есть при таких условиях не допускаются никакие самопроизвольные смены значений битов, вызванные отсутствием обновления. Однако нападающему необязательно получать каждый отдельный бит, чтобы получить хранящиеся в RAM секреты: успешной такой может считаться получение любых данных. Кроме того, tREFI определён для крайних значений допустимых диапазонов (наибольшей температуры и частоты), а DRAM обычно работает в более спокойных условиях. После отключения питания тактовые генераторы не работают, поэтому влияние частоты, в основном вызванное электромагнитными помехами между ячейками и линиями, больше не имеет воздействия. Поэтому tREFI не может быть хорошей аппроксимацией для определения скорости затухания зарядов данных, которая способна быть существенно ниже.
Процесс тестирования
Если вкратце, итерация тестирования выглядит так:
- заполняем RAM по известному паттерну,
- отключаем питание,
- снова подаём питание после переменных периодов времени,
- сравниваем содержимое RAM с ожидаемым паттерном.
Существуют некоторые тонкости, требующие выполнения дополнительных шагов, не связанных напрямую с нашей главной целью. Например, мы можем не использовать всю RAM — прошивка и код, выполняющий запись и сравнение паттерна, тоже расположены в RAM, и эти части памяти необходимо распознавать и пропускать.
Различные факторы среды, например, температура (как окружающей среды, так и DRAM), влажность воздуха и внешние источники тепла могут быть причинами ошибочных результатов. Запись в память временно повышает её температуру, поэтому перед отключением питания мы будем оставлять её охладиться, чтобы все итерации тестов выполнялись в одинаковых начальных условиях.
Предполагается, что изначальное значение ячейки DRAM случайно. Даже спустя бесконечное время каждый бит имеет ту же вероятность смены значения, что и вероятность сохранения записанного значения. Это означает, что максимальный возможный результат — это 50% поменявших значение битов, не считая статистических ошибок. Чтобы избежать любого влияния паттерна на другие биты, мы будем при помощи регистра сдвига с линейной обратной связью (linear-feedback shift register, LFSR) генерировать псевдослучайный паттерн, порождающим значением для которого будет адрес памяти. LFSR обеспечивает результаты с достаточно равномерным распределением, требуя при этом очень мало вычислений.
Для тестирования мы разработали приложение, записывающее сгенерированный паттерн и сравнивающий его при следующем запуске системы. Перед проектированием приложения были приняты следующие допущения:
- Работа с физическими адресами с неограниченным доступом к памяти. Операционная система из-за её изоляции памяти усложнила бы работу. Поэтому приложение, работающее с «голым железом», проще спроектировать и разработать, чем работающее в какой-нибудь операционной системе.
- Получение схемы распределения доступной памяти. Как говорилось выше, доступна не вся память. Код не может переписывать себя или созданные прошивкой сервисы. Чтобы сделать это, у нас должна быть возможность запрашивать и парсить схему распределения памяти прошивки, которая определяет, какие интервалы памяти необходимо оставить неизменными.
- Для записи в память и сравнения используется один и тот же двоичный файл. Это косвенно связано с предыдущим требованием. Если бы мы написали отдельные приложения для записи и сравнения, то они, скорее всего, имели бы разные размеры, что отразилось бы на схеме распределения памяти. Ещё одна причина создания одного общего приложения заключается в очень схожей функциональности, за исключением того, что одна часть кода будет выполнять считывание, а другая запись. Обе должны иметь возможность получения схемы распределения памяти, её парсинга, генерации идентичных паттернов и доступа к памяти.
- Память не должна распределяться динамически. Это ещё одно требование для обеспечения постоянства схемы распределения памяти. Динамическое распределение памяти не отражается в изначальной схеме распределения. Все данные должны распределяться статически, а если это невозможно (например, часть необходимых сервисов прошивки распределяет память), то схему распределения памяти нужно получать после выполнения вызовов всех сервисов.
- Генерация псевдослучайных паттернов для записи и сравнения.
- Приостановка выполнения в заранее заданных точках. Это позволяет выполнять непрерывный мониторинг температуры, если потребуется охлаждение DRAM до ожидаемых значений.
- Отображение прогресса записи и сравнения содержимого памяти. Время доступа к RAM относительно равномерно, поэтому достаточно показывать прогресс в процентах. В основном это нужно для того, чтобы понимать, что приложение не зависло (при больших размерах RAM её выполнение может занимать достаточно долгое время), а также для примерной оценки времени, требуемого для каждой итерации.
- Сбор статистики о битах, поменявших своё значение. Наряду с общим количеством поменявших значение битов нам нужно собирать статистику по битам, поменявшим значение в каждой линии шины данных DIMM. Из-за архитектуры DIMM каждая такая линия всегда связана с одинаковой DRAM, вне зависимости от адреса.
- Вывод результатов в текстовом виде. Приложение должно выводить результаты и состояние оператору на экран и/или через UART. Кроме того, результаты должны сохраняться в хранящийся рядом с приложением файл для последующего анализа.
На основании всего вышеперечисленного мы решили реализовать код в виде приложения UEFI, воспользовавшись gnu-efi
. Все представленные выше требования или обрабатываются сервисами UEFI, или легко реализуются на C. Код выложен в репозиторий Dasharo/ram-remanence-tester. В файле README представлена информация о сборке и запуске.
Первые проблемы и изменения в исходном плане
Первые прогоны тестов в QEMU выявили некоторые проблемы. Хотя некоторые диапазоны памяти были помечены как доступные, их содержимое менялось при горячем перезапуске. Причина заключалась в том, что сама прошивка способна распределять память, использовать её и освобождать до запуска приложения. Чтобы обойти эту проблему, мы добавили ещё один этап приложения: после записи паттерна выполняется горячий перезапуск, после чего память сравнивается с ожидаемым паттерном. Если они не совпадают, то вся страница целиком удаляется из тестируемых диапазонов. После этого можно продолжать настоящее тестирование, запланированное изначально.
Ещё одна проблема заключалась в том, что схема распределения памяти иногда менялась. В большинстве случаев она была согласованной, но примерно при каждом десятом прогоне приложение загружалось в другой адрес, скорее всего, потому, что по неизвестным причинам менялось другое распределение. Чтобы обойти эту проблему, мы отфильтровали области меньше 16 МБ и выстроили оставшиеся по основанию 16 МБ и размеру, кратному этому значению. Это не устраняет первопричину проблемы, но существенно снижает вероятность того, что она нам помешает.
После нескольких первых попыток мы также отказались от точного измерения температур DRAM. Они были относительно постоянными и очень близкими к температуре окружающей среды десктопа, а на ноутбуке они постепенно становились выше, даже в режиме простоя. Мы подумали, что это может быть вызвано расположенным поблизости GPU, но пока не тестировали похожего ноутбука без графической карты.
Что касается температуры окружающей среды, то первые итерации прогонов выполнялись в нашем офисе, где температура моментально поднималась до 24 C. Для ноутбука было невозможно получить никаких разумных данных в таких условиях, поэтому мы перенесли тестирование в лабораторию с кондиционированием воздуха, где температуры колебались в пределах 19-20 C.
Тестируемые платформы
Мы решили протестировать две платформы, отличающиеся тем, что одна из них была ноутбуком, а вторая настольным PC, и в одной была установлена память DDR5, а в другой — DDR4. Из-за этого их непосредственное сравнение не имело смысла, да и не было нашей целью.
Первой тестируемой платформой стал ноутбук NovaCustom V540TND с DDR5 SODIMM W-NM56S508G. В настольном PC была установлена память DDR4 MSI PRO Z790-P WIFI с DIMM Kingston KF432C16BB/4. Как видите, мы выбрали для тестирования малый размер памяти. Хотя при большем количестве модулей памяти результаты могут немного отличаться из-за худшего воздухотока, мы хотели сэкономить время, потому что вообще не были уверены в получении полезных результатов.
Если статья будет интересна читателям, в будущем мы можем расширить тестирование и рассмотреть другие конфигурации оборудования.
Результаты
Ниже показаны результаты, разделённые по платформам, что совпало с разделением на поколения DDR. При всех тестах влажность воздуха была постоянной (33%). Температура несущественно менялась, она указана рядом с результатами.
В результатах показано количество изменившихся битов, разделённое между переходами из 1 в 0 и из 0 в 1, а также среднее двух переходов. На горизонтальной оси отложен номер бита на шине памяти.
▍ Ноутбук — DDR5 SODIMM
На этой платформе нам с трудом удавалось получить хоть какие-нибудь сохранившиеся биты. В конечном итоге мы отсоединили аккумулятор, а затем отключали и максимально быстро подключали обратно кабель питания. Время без питания было настолько коротким, что его невозможно было измерить при помощи секундомера.
Наши лучшие три замера были равны 36,06%, 39,28% и 41,04%, а остальные оказались столь близкими к наихудшему возможному результату (50%), что их вполне можно отбросить как статистическую ошибку. Все данные оказывались утерянными спустя одну секунду после отключения питания.
20,1 C, время без питания примерно 0 с, 36,06% изменившихся битов
19.3 C, время без питания примерно 0 с, 39.28% изменившихся битов
19.4 C, время без питания примерно 0 с, 41.04% изменившихся битов
▍ Настольный PC — DDR4 DIMM
Эта платформа продемонстрировала гораздо более высокие показатели сохранения данных. Мы выполняли замеры с интервалами по 10 секунд, начиная с мгновенного включения питания и до 2 минут.
19.8 C, время без питания примерно 0 с, 0.08% изменившихся битов
19.5 С, время без питания 10 с, 6.32% изменившихся битов
19.4 C, время без питания 20 с, 13.63% изменившихся битов
19.4 С, время без питания 30 с, 23.39% изменившихся битов
19.3 С, время без питания 40 с, 35.01% изменившихся битов
19.4 С, время без питания 50 с, 42.81% изменившихся битов
19.3 С, время без питания 60, 46.22% изменившихся битов
19.3 С, время без питания 70 с, 47.71% изменившихся битов
19.2 С, время без питания 80 с, 48.57% изменившихся битов
19.2 С, время без питания 90 с, 48.96% изменившихся битов
19.2 С, время без питания 100 с, 49.26% изменившихся битов
19.1 С, время без питания 110 с, 49.43% изменившихся битов
19.2 С, время без питания 120 с, 49.51% изменившихся битов
Получив эти замеры, можно построить график сохранившихся битов в зависимости от времени без питания:
Заключение
Обе платформы демонстрируют, что со временем данные теряются, но скорость на протестированных платформах существенно различается. Тех показателей, для которых хватало меньше секунды на ноутбуке с DDR5, нельзя было получить даже спустя две минуты на десктопе с DDR4.
На обеих платформах заметно влияние топологии DIMM. В случае DDR5 SODIMM распределение изменений в битах 0-31 приблизительно согласуется с распределением изменений в битах 32-63, особенно для средних значений. В младшей половине больше переходов из 0 в 1, что в старшей половине зеркально отражается в переходах из 1 в 0. Видна и граница между DRAM, хоть она и не так заметна, как в ситуации с DDR4 DIMM. Результаты последней демонстрируют чёткое различие между группами из 8 последовательных битов, а среднее в группе остаётся практически постоянным.
В течение одного прогона некоторые DRAM начинают терять свои данные быстрее остальных. Нельзя сказать, что это коррелирует с их максимальным временем сохранения — взгляните на группу битов 40-47 в DDR4. При задержках включения питания в диапазоне 10-50 секунд эта группа всегда теряет наибольшую часть своего содержимого, но после ситуация не ухудшается так же быстро, как в других группах.
Для DDR4 график процента утерянных данных в функции от времени демонстрирует, что количество утерянных данных быстро растёт при малых задержках, но потом замедляется, когда приближается к 50%. Кроме того, график сглажен слева, это может говорить о том, что к измерениям времени добавляется какая-то постоянная систематическая погрешность. На самом деле, после подачи питания на этой платформе светодиод питания загорается спустя примерно 5 секунд.
Подведём итог
Мы узнали, что для утери содержимого RAM требуется время, и это время варьируется для протестированных конфигураций. К сожалению, у нас недостаточно примеров данных, чтобы определить, что вызывает такие различия. Мы бы хотели расширить это исследование, в будущем включив в него новые платформы и различные DIMM.
Telegram-канал со скидками, розыгрышами призов и новостями IT 💻