Skip to content

Double-sided Rowhammer

Bir victim row'u (row N) kuşatan iki aggressor row'u (N-1 ve N+1) hammer'la; disturbance'ı row N üzerinde yoğunlaştırarak bit'lerini single-sided hammering'e göre çok daha hızlı ve güvenilir biçimde flip et.

Mechanism

Kurbanı kuşatmak neden işe yarar

Rowhammer bit flip'leri, bir aggressor row'un tekrar tekrar activation'ından doğar; bu da fiziksel olarak komşu row'lardan charge sızdırır. En çok etkilenen row, bir aggressor'a hemen bitişik olandır. Bir hedefin iki tarafına iki aggressor yerleştirebilirseniz — victim row N etrafında N-1 ve N+1 row'ları — o zaman row N aynı anda her iki komşusundan disturbance alır. Bu "double-sided" düzenleme, tek bir komşuyu uzak bir row'a karşı hammer'lamaya (single-sided / amplified hammering) göre daha fazla flip üretir ve bunları çok daha az activation ile üretir.

Püf noktası: aggressor'ları N-1 ve N+1'e yerleştirmek için hangi physical address'lerin aynı bank'in adjacent row'larına denk geldiğini bilmeniz gerekir — farklı row'lar (ki her access row'u yeniden activate etsin) ama aynı bank (ki tek bir row buffer üzerinde çekişsinler). Bu, DRAM bank/row addressing reverse-engineering ile elde edilen DRAM address mapping'ini gerektirir.

Hammering loop'unun kendisi CPU cache'ini yenmek zorundadır: cache'ten servis edilen read'ler asla DRAM'e ulaşmaz, bu yüzden her access'in ardından gerçek bir row activation'ı her iterasyonda zorlamak için clflush gelir (ya da bir eviction set / non-temporal access kullanılır).

Walkthrough

1. The core hammer loop (Seaborn/Dullien). Aynı bank'in farklı row'larına işaret eden iki pointer, her biri önce read sonra flush edilerek, sıkı bir loop içinde:

code1a:
  mov (X), %eax     ; activate row containing X
  mov (Y), %ebx     ; activate row containing Y
  clflush (X)       ; evict so next read re-activates
  clflush (Y)
  jmp code1a

X ve Y aynı bank'te farklı row'lara map olmak zorundadır. Aynı row → row buffer her iki read'i de re-activation olmadan servis eder; farklı bank'ler → tek bir row'un sürekli hammering'i olmaz.

2. Make it double-sided. Bir aggressor artı uzak bir row yerine, X ve Y'yi bir victim row'u çevreleyen row'lar olacak şekilde seçin. Project Zero, memory'yi üç eşit region olarak düzenledi ve dıştaki ikisini hammer'ladı:

[ aggressor region ][ victim region ][ aggressor region ]
   256k                 256k             256k
       row N-1            row N             row N+1

İki dıştaki read ortadaki victim'i kuşatır, dolayısıyla flip'ler victim region'ında yoğunlaşır.

Templating run (kavramsal çıktı)
$ ./rowhammer-test --double-sided
found aggressor pair: 0x... / 0x...  (same bank, rows N-1,N+1)
hammering 1,000,000 iterations...
victim region: bit flip at offset 0x1a3c  0xFF -> 0xFE
flips this pass: 7

Sadece virtual adjacency değil, DRAM mapping'ine ihtiyacınız var

Virtual olarak (ve hatta physical olarak) contiguous page'ler aynı bank'te row-adjacent değildir — memory controller'ın bank/row hash'i onları dağıtır. X ve Y'yi yalnızca physical-address aritmetiğiyle seçmek sessizce yanlış row'ları veya farklı bank'leri hammer'lar ve hiç flip üretmez. Önce mapping'i elde edin ya da same-bank adjacency'yi row-buffer conflict timing ile doğrulayın.

clflush genellikle bunu mümkün kılan şeydir

Klasik loop, clflush'ın user space'ten erişilebilir olmasına bağlıdır. Bunun kısıtlandığı yerlerde (ör. JavaScript, bkz. rowhammer.js) flush, activation başına daha yavaş olan cache eviction set'leriyle değiştirilir.

Mitigation

  • Target Row Refresh (TRR) ve in-DRAM tracker'lar, aggressor'lar over-activate edildiğinde olası victim'leri refresh eder (sonraki many-sided pattern'lerle bypass edilir; bkz. TRRespass, Blacksmith).
  • ECC single-bit flip'leri düzeltir (ama multi-bit flip'ler ve ECC side effect'leri exploit edilebilir kalır).
  • Artırılmış refresh rate (ör. 2x) charge leakage için pencereyi kısaltır.

References