Perché le CPU operano speculativamente con i risultati di recuperi di memoria proibiti?

6

A mio parere, gli attacchi Meltdown e Spectre sfruttano entrambi il fatto che alcuni processori moderni, quando vengono dati qualcosa del tipo:

if (x < arr1[y])
  z = arr2[arr3[x]*256];

a volte può recuperare il valore di arr2[arr3[x] * 256] prima che abbiano determinato se x è inferiore a arr1[y] , e può farlo senza considerare se il codice ha un'autorizzazione adeguata per accedere a arr3[x] . La parte in corsivo è il sine qua non della vulnerabilità: se il processore recuperava solo arr2[arr3[x] * 256] nei casi in cui l'accesso a arr3[x] era ammissibile, sarebbe impossibile sfruttare un arr2[___ * 256] che utilizza valori di arr3[x] caricati illegittimamente perché non ce ne sarebbero.

Ha senso che anche se arr3[x] non è valido, il processore non può intercettare a meno che o fino a quando non determini che x è inferiore a arr1[y] . Quello che non riesco a capire è perché un recupero speculativo da un indirizzo non valido non dovrebbe indurre la CPU ad abbandonare il percorso di esecuzione speculativo corrente? Penserei che in quasi tutti gli scenari realistici accadrebbe una delle due cose:

  1. La previsione del ramo che ha portato all'esecuzione speculativa risulta errata, nel qual caso qualsiasi lavoro che potrebbe essere eseguito con il valore recuperato in modo speculativo dovrà essere scartato.

  2. La previsione del ramo che ha portato all'esecuzione speculativa risulta essere corretta, nel qual caso l'esecuzione dovrebbe intercettare l'accesso non valido, e il lavoro che potrebbe essere fatto con il valore recuperato in modo speculativo (prima di eseguire la trappola) deve essere scartato.

Esiste uno scenario realistico in cui il lavoro speculativo che segue un recupero speculativo da un indirizzo illegittimo potrebbe rivelarsi utile? In caso negativo, quale vantaggio c'è per consentire all'esecuzione speculativa di continuare oltre tali recuperi? Se un recupero non valido farà abbandonare la CPU alla linea corrente di esecuzione speculativa, ciò eviterebbe la necessità di tenere traccia delle trappole in sospeso speculative.

    
posta supercat 05.01.2018 - 00:27
fonte

2 risposte

3

Considera il seguente codice:

mprotect(arr3, sizeof(arr3), PROT_NONE);
...
mprotect(arr3, sizeof(arr3), PROT_READ);
z = arr2[arr3[x]*256];

Ad un certo punto, arr3 diventa leggibile dal processo. Se il compito (beh, in realtà il dereferenziamento di arr3) è già stato caricato prima del completamento di mprotect, quale comportamento ti aspetteresti? Non mi aspetterei una trappola, dal momento che è valida al punto.

Fondamentalmente, non sai quale sia lo stato della memoria in arr3 finché l'istruzione precedente non è stata ritirata, a quel punto la pipeline sarà stata eseguita ben oltre quel punto.

Suppongo che potresti "speculativamente intrappolare" e rieseguire se le autorizzazioni di memoria cambiano nel frattempo, ma a quanto pare non è ciò che viene fatto oggi. (Anche se immagino che sarebbe un colpo di prestazioni molto più piccolo rispetto alle correzioni del software KPTI.)

    
risposta data 05.01.2018 - 01:52
fonte
0

Ovviamente è una cattiva idea fare qualcosa con il risultato di un carico speculativo di una locazione di memoria che il codice non è autorizzato a leggere. Ma non puoi semplicemente proibire di usare i dati da una locazione di memoria che non ti è stato permesso di leggere, perché spesso non lo sai (ancora). Dovresti vietare l'uso di dati da una posizione di memoria in cui non hai ancora capito se sei autorizzato a leggerlo o meno.

Sfortunatamente, i dati di altri processi possono essere effettivamente nella cache L1. Quindi, se si legge dalla cache L1 (che è molto veloce), il processore non sa ancora se è stato permesso di accedere ai dati o meno. Quindi dovresti aspettare praticamente tutti gli accessi alla memoria fino a quando non vengono controllati i permessi, il che rallenterebbe considerevolmente le cose. Bene, almeno con il modo in cui Intel ha progettato i loro processori. Poiché i processori AMD non funzionano alla velocità di una lumaca, ovviamente stanno facendo qualcosa diversi.

Un metodo (che richiede un cambiamento sostanziale nel processore) sarebbe quello di assicurarsi che, sebbene i dati di un altro processo possano essere nella cache L1, tali dati produrranno sempre un errore di cache (se si cambiano i processi, quindi gli stessi dati Magicamente non produrrebbe più errori di cache). In questo modo, puoi tranquillamente utilizzare i dati dalla cache L1 in modo speculativo e, se non è nella cache L1, potresti avere abbastanza tempo per controllare le autorizzazioni prima che arrivino i dati.

Un altro metodo sarebbe quello di interrompere l'esecuzione speculativa quando succede qualcosa che potrebbe causare cambiamenti rilevabili. Ad esempio, dopo una lettura speculativa, è possibile continuare finché non si incontra un'operazione che potrebbe modificare la cache in qualsiasi modo.

    
risposta data 07.01.2018 - 21:02
fonte

Leggi altre domande sui tag