«Железная» уязвимость в DRAM позволяет изменить содержимое чужой памяти

    Опубликована статья Yoongu Kim & others. Flipping Bits in Memory Without Accessing Them: An Experimental Study of DRAM Disturbance Errors, описывающая способ изменения содержимого DRAM памяти, не требующий доступа по этому адресу. Фактически это означает нарушение изоляции памяти между процессами или виртуальными машинами. Из проверенных 129 модулей памяти, 110 оказались подвержены уязвимости, в том числе, все модули, выпущенные после 2012 года.


    Организация DRAM



    dram structure
    DRAM представляет собой двумерную решётку, в узлах которой находятся ячейки памяти, каждая из которых хранит один бит. Каждая ячейка состоит из транзистора, и конденсатора, который может быть заряжен или не заряжен, что соответствует значению бита 1 или 0. Конденсаторы со временем теряют заряд, так что необходимо периодически (раз в несколько десятков миллисекунд) перезаписывать информацию (регенерация). В современных чипах для улучшения производительности делают несколько независимых модулей («банков»), с отдельными выходными каскадами.

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

    Оказалось, что если периодически включать и выключать wordline, наведённые токи приводят к увеличению утечки в соседних ячейках того же банка, и если сделать много переключений между циклами регенерации, этого может быть достаточно для переключения бита с 0 на 1 или наоборот.

    Демонстрация



    Эффекта можно достичь с помощью кода, не требующего никаких специальных привилегий. Простейшая атака выглядит так:

    code1a:
      mov (X), %eax  ; прочитать адрес X
      mov (Y), %ebx  ; прочитать адрес Y
      clflush (X)    ; сбросить строку кэша, соответствующую адресу X
      clflush (Y)    ; сбросить строку кэша, соответствующую адресу Y
      mfence         ; дождаться окончания операций с кэшем
      jmp code1a
    


    Адреса X и Y должны быть в одном банке, но в разных строках DRAM. Сброс кэша нужен, чтобы гарантировать чтение из RAM на каждом цикле. Два адреса надо использовать, чтобы обеспечить включение/выключение wordline на каждом цикле. Этот код не вызывает уязвимость, поскольку логика DRAM оптимизирует включение wordline, и необходимых постоянных переключений не происходит:

    code1b:
      mov (X), %eax
      clflush (X)
      mfence
      jmp code1b
    


    Чтобы вызвать ошибку, необходимо сделать несколько сотен тысяч циклов за время между двумя регенерациями (обычно 64 мс), что вполне достижимо. Наличие ECC не сильно помогает, так как нередки ошибки одновременно в нескольких битах.

    Similar posts

    Ads
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More

    Comments 31

      +1
      Сложно назвать это уязвимостью.
      Предсказать адреса доступные из user-mode которые мапятся физически рядом с целевым адресом практически невозможно.
        +7
        Это вполне такая реализуемыая DoS-атака. Представьте себе: облачный сервис, кто-то особо вредный арендовал машину и херачит вот такую штуку, а у других клиентов то софт падает, то данные портятся ни с того, ни с сего.
          +10
          Линейки не слишком болшие, к примеру для 8Gb-плашки это 12бит на адрес колонки, то есть 512байт в строке.
          Уязвимы лишь соседние строки. При этом память адресуется по-странично, причём страница как минимум в несколько раз больше строки.
          То есть всё же вероятность того что 2 соседних строки принадлежат разным приложениям/виртуальным машинам очень и очень мала.
          0
          Достаточно поменять адрес доступный из user-mode для чтения. Например libc.so которая замаплена в множество рутовых процессов.
            0
            А что это меняает?
            То есть менять саму libc не получится, так как как минимум copy-on-write.
            Поэтому нужно найти адрес который будет лежать прямо перед либо после libc.
            С учётом того что я думаю что libc мапится в начале загрузки системы, то физически она расположена достаточно рано, и вероятность того что доступная нашему процессу память будет рядом с ним — ничтожна.
              0
              Код на запись обычно не доступен совсем. Даже если бы в libc можно было писать и она была copy-on-write, это бы не помогло. Процессор ничего про copy-on-write не знает. Копирование выполняет операционная система, которая перехватывает исключение возникающее при попытке записи в память доступную только для чтения. Если же память изменяется при чтении, то никакого исключения и как следствие копирования не будет.
                0
                Собственно я об этом и написал, возможно не так четко. но «менять саму libc не получится».
                  0
                  В физической памяти находится только одна копия libc, libc изменяется, никаких копирований не происходит -> «значит менять саму libc не получится». Странная у вас логика.
                    0
                    Довольно простая.

                    «так как как минимум copy-on-write» => наименее строгое правило работы с памятью библиотек — это copy-on-write, остальные принципы еще более строги, поэтому смотрим на наиболее благоприятный для эксплуатации copy-on-write => модифицировать оригинальный libc, доступный остальным рутовым процессам не получится, так как страница памяти будет скопирована в АП процесса, который выполняет данную модификацию, и не затронет общие страницы libc.

                    В каком месте я утверждаю что есть несколько libc?
                      0
                      Еще раз.
                      1. Есть только одна копия libc.
                      2. Процесс увидит как libc изменяется.
                      3. Никакого копирования не будет ни для read-only, ни для copy-on-write памяти:
                      Код на запись обычно не доступен совсем. Даже если бы в libc можно было писать и она была copy-on-write, это бы не помогло. Процессор ничего про copy-on-write не знает. Копирование выполняет операционная система, которая перехватывает исключение возникающее при попытке записи в память доступную только для чтения. Если же память изменяется при чтении, то никакого исключения и как следствие копирования не будет.

                        0
                        Ок. И с каким же моим утверждением вы спорите?
                          0
                          Откуда непосредственно следует, что удастся менять саму libc.
                            0
                            аа, наконец-то дошло до меня.
                            > Достаточно поменять адрес доступный из user-mode для чтения
                            Смутил первый коммент, исходя из которого я подумал что предлагаете изменять память libc.
                            Да, тогда беспрестранное чтение libc повлечёт за собой (если верить статье) порчу соседних строк во всех процессах.
          –18
          В компьютере еще полно таких наводок. Поэтому и введены контрольные суммы… Не думаю, что производителям интересно их исправлять — они гонят версии и набивают карманы, а не улучшают железо…
            –13
            П… ец
              –3
              Боже в какой капец Хабр скатился. Занудство 146%. А ведь когда-то мне тут нравилось
              –1
              я бы удивился если бы Вам тут наплюсовали) Кажется, начинаю понимать мышление накармленных хабровцев :)))
              P.s. +1 комменту
              +7
              Теоретически возможно, но есть вещи которые они просто утаили и эти моменты делают атаку не возможной на реальной системе:
              1 Для тестов они использовали FPGA контроллер Xilinx, а не релаьный контроллер используемый в ПК.
              2 Не учитывают что не одни они используют оперативную память. Т.е. даже если у вас однопроцессорная система, в ней должны быть отключены DMA и все другие потребители способны самостоятельно обращаться к памяти.
              3 Забыли про механизм регенерации. Если self-refresh включен, то можно про атаку так же смело забыть.
                +2
                1. FPGA там в основном для повторяемости эксперимента.
                2. Отключать других потребителей не надо — достаточно успеть сделать много циклов чтения между двух циклов регенерации.
                3. Self-refresh, насколько я понимаю, рефрешит текущую строку после чтения, а соседи остаются уязвимыми.
                  0
                  После чтения строку обязательно требуется записать снова, self-refresh совсем не о том.
                    0
                    Сори! Замкнуло. Хотел сказать auto refresh.
                    Self refresh это же для суспенд режимов.
                –2
                Если честно, то бред. Редкостный бред.
                Т.е. эти граждане утверждают, что из памяти при простом непрерывном чтении периодически читаются невалидные данные? При этом тестами это не обнаруживается?
                Собственно, каждый сам подумать, на сколько он в это верит.
                И ещё одинм факт — нормальные тесты полностью отключают кэширование при тесте. Даже не нужно кэш каждый раз сбрасывать! Странно, что они об этом не знают. А чтобы исполняемый код на память не влиял, код и стек «защёлкиваются» в кэше процессора.
                И естественно, что тесты гоняют паттерны, которые подобные проблемы выявляют.
                И очевидно, что тесты ничего подобного на нормальной памяти не выявляют.
                Т.к. тайминги устанавливаются с учётом подобный эффектов и цикл регенерации, очевидно выбирается из предположения, что вообще непрерывно идут обращения.
                А описываемый эффект достижим, но только нужно тайминги неправильные выставить, что в реальности может быть лишь в случае бракованной памяти или ошибки в BIOS. Не зря они это на FPGA гоняют.
                И, кстати, в случае наличия присутствия эффекта легко лечится уменьшением периода регенерации.
                А с неправильными таймингами и не такие чудеса возможны.
                  0
                  Господа минусующие, Вы правда считаете, что если на реальной системеотключить кэширование и непрерывно читать по кругу два адреса, то память будет выдвать невалидные данные?
                  Серьёзно?
                  +1
                  Очень сильно напоминает атаки по ошибкам вычислений (Fault attacks, краткое описание в pdf), просто в данном случае ошибка вводится в память. С помощью ошибок можно изменить данные регистров, пропустить функции, да и вообще натворить множество интересных вещей. Мне нравится эта работа лишь тем, что для ввода ошибок они не используют дополнительного оборудования.
                    0
                    Может это и не уязвимость — но что это полный пэ, так это точно. Не должна прикладная программа иметь доступ к таким вещам, как организация памяти. Я даже не понимаю: а как-то скорректировать это, не переходя на статическую память, возможно?
                      0
                      Да, проблема реальная и очень нехорошая — из-за того, что фикс в старых системах, может быть только за счет очень сильной потери по производительности.

                      Фикс на стороне контроллера уже придуман и запатентован, так что в новых системах на основе процессоров Интел — все по идее должно быть хорошо.

                      www.google.com/patents/US20140006704
                      www.google.com/patents/US20140095780
                      www.google.com/patents/WO2014004748A1
                        0
                        Вот и вся разгадка теории о влиянии космических лучей на оборудование…
                          0
                          Что то не фига не получается на КР565РУ5Г повторить.
                            0
                            Написал userspace тестер, Кушает память — так надо.
                            На моем домашнем серваке i3+DDR3 4 гига одной планкой. Память гарантированно портится до 12-13 итерации.
                              0
                              MBP 17" Late 2011, память двумя плашками по 8 ГБ, 1333 МГц, без ECC, ничего не испортилось, вместо 33-й итерации написало, что у меня прекрасно. Собирал gcc -O0, в процессе теста переключал приложения и компьютером всячески пользовался.
                              0
                              Собирал с дефолтовыми ключами.
                              По идее чтобы из юзерспейса замаллочить непрерывный кусок памяти в пределах одной микросхемы нужно чтобы луна была в 3ей четверти, марс в скорпионе и память не слишком фрагметированна. Я вообще удивлен что у меня срабатывает иногда.
                              У меня это выглядит обычно так… Хотя 1 раз было что все 32 пасса прошли без ошибок. Тут нужна определенная доля везения невезения.
                              root@slon-P5Q:~/WORK/tandemx# ./a.out
                              #0… OK
                              #1… OK
                              #2… OK
                              #3… OK
                              #4… OK
                              #5… DRAM CRC FAIL! ;-)
                              root@slon-P5Q:~/WORK/tandemx#./a.out
                              #0… OK
                              #1… DRAM CRC FAIL! ;-)
                              root@slon-P5Q:~/WORK/tandemx#./a.out
                              #0… DRAM CRC FAIL! ;-)

                              Only users with full accounts can post comments. Log in, please.