hackweb
appunti di hacking e tecnologia
IT EN
menù principale
menù utente
non sei loggato

Spectre: leggere memoria che la CPU non ha mai davvero letto

20 maggio 2026 · 3 min di lettura · #hardware #side-channel #cpu

Le CPU moderne non aspettano. Quando incontrano un branch condizionale — un if — non si fermano a calcolare la condizione: il predittore di salto indovina l'esito e la CPU inizia a eseguire il ramo previsto in speculazione, in anticipo. Se ha indovinato, ha guadagnato tempo. Se ha sbagliato, butta via i risultati e riparte dal ramo giusto. Architetturalmente è come se nulla fosse successo.

Il problema, scoperto nel 2018, è quel quasi. Le istruzioni speculate e poi annullate lasciano un effetto collaterale che l'annullamento non ripulisce: lo stato della cache. Spectre legge quello stato e ci ricava i dati che la speculazione ha toccato.

Il gadget vulnerabile

Il caso canonico, Spectre variante 1 (bounds check bypass):

if (x < array1_size) {
    y = array2[array1[x] * 4096];
}

Il controllo x < array1_size sembra proteggere l'accesso. Ma se array1_size non è in cache, la CPU ci mette centinaia di cicli a caricarlo. Nell'attesa, il predittore — allenato da noi con tanti x validi — assume che il ramo si prenda, e specula. Con un x malevolo e fuori dai limiti, durante la finestra speculativa la CPU legge davvero array1[x], un byte di memoria arbitraria, e lo usa come indice in array2.

Il segreto entra nella cache

Il trucco è la moltiplicazione per 4096: la dimensione di una pagina. Il byte segreto b = array1[x] determina quale pagina di array2 viene portata in cache dalla lettura speculativa. Ogni valore possibile di b (0–255) tocca una pagina diversa e distante, così non c'è prefetch che confonda le acque.

Poi la CPU si accorge che x era fuori limite, annulla tutto, e y non viene mai scritto architetturalmente. Ma array2[b*4096] è rimasto in cache.

Flush + Reload: leggere la traccia

Prima dell'attacco svuoti dalla cache tutte le 256 pagine candidate (clflush). Dopo la speculazione, misuri quanto tempo serve a leggere la prima parola di ciascuna:

for (i = 0; i < 256; i++) {
    addr = &array2[i * 4096];
    t0 = rdtscp();
    tmp = *addr;              // lettura
    dt = rdtscp() - t0;       // ciclo di misura
    if (dt < CACHE_HIT_THRESHOLD)
        results[i]++;         // questa pagina era in cache -> b == i
}

La pagina che si legge in ~50 cicli invece di ~300 è quella portata in cache dalla speculazione: il suo indice è il byte segreto. Ripeti la misura più volte per battere il rumore, avanzi x di un byte, e leggi memoria a piacere — un byte alla volta, tipicamente a qualche KB/s.

Perché è tanto grave

Spectre non sfrutta un bug del software: sfrutta il comportamento corretto della microarchitettura. Il codice del gadget è privo di errori secondo qualsiasi analisi statica. E soprattutto attraversa confini che consideravamo solidi:

  • Sandbox del browser. La versione originale leggeva memoria del processo del browser da JavaScript, allenando il predittore con codice JS. La risposta è stata togliere ai siti i timer ad alta risoluzione (performance.now() depotenziato, SharedArrayBuffer disabilitato per anni) e introdurre l'isolamento dei siti in processi separati.
  • Kernel da user space e guest → host nella virtualizzazione, quando il gadget giusto esiste nel codice privilegiato.

Le mitigazioni e il loro prezzo

Non esiste una patch software unica perché la radice è nel silicio. Si combatte su più fronti, e ognuno costa cicli:

  • Barriere di speculazione. Su x86 lfence dopo il controllo dei limiti impedisce alla CPU di speculare oltre. Il compilatore può inserirle, ma serializzano l'esecuzione: lento se abusato.
  • Clamp dell'indice. Invece di affidarsi al ramo, si maschera l'indice in modo che resti nei limiti anche in speculazione (array_index_nospec nel kernel Linux).
  • Retpoline per la variante 2 (branch target injection): sostituisce le chiamate indirette con un costrutto che intrappola la speculazione in un loop innocuo invece di lasciarla saltare a un target avvelenato.

Il conto complessivo, tra Spectre, Meltdown e i loro discendenti (MDS, L1TF, Retbleed), è stato una perdita di prestazioni misurabile su carichi reali — in certi casi a due cifre percentuali — pagata da ogni datacenter del pianeta. È il caso di studio definitivo del fatto che l'astrazione della CPU è una bugia utile: sotto, il tempo e la cache raccontano tutto.


« torna alla home

ultimi post
 
il tuo indirizzo IP:
216.73.216.108
visitatore #0
MOTD:
Ogni astrazione perde da qualche parte.
Qui guardiamo dove.
argomenti