Poiché i corrispondenti documenti di ricerca offrono descrizioni abbastanza esplicite pubblicamente, suppongo che pubblicare il mio codice qui sotto non sia considerato come incoraggiare o sostenere gli exploit. Tuttavia, sono consapevole che alcuni rispondenti potrebbero preferire rimanere un po 'vaghi ...
Mi sono confuso da vari test se i miei sistemi non sono protetti contro le vulnerabilità di Spectre (e Meltdown) dopo l'applicazione delle patch: a volte sembravano in disaccordo o non era chiaro se un ulteriore software necessitasse ancora di un altro patch. Pertanto, intendevo fare i miei test sfruttando effettivamente le vulnerabilità, in contrasto con la maggior parte dei suddetti strumenti di test che leggono piuttosto vari tipi di informazioni su CPU e stato.
Considera il seguente frammento di codice ...
#define CACHELINESIZE 4096
typedef char line[CACHELINESIZE];
line A[512];
char secret = 'H';
char any = 'X';
#define REP 100
char* pcheck[REP];
line* pwrite[REP];
for (int i=0; i<REP; i++) {
pcheck[i] = &any;
pwrite[i] = A;
}
pcheck[REP-1] = &secret;
pwrite[REP-1] = A+256;
char dummy;
for (int i=0; i<REP; i++) {
if (i != (REP-1)) {
dummy = pwrite[i][*pcheck[i]][0];
}
}
Il if
valuta in true
99 volte in una riga (causando un line
determinato da other
da recuperare nella cache e da leggere da) e poi una volta su false
. Quindi presumo che nell'ultima esecuzione, la previsione del ramo sia ingannata nell'esecuzione speculativamente (ma in seguito abbandonando) il compito. In particolare, un line
determinato dal contenuto di secret
viene recuperato nella cache, anche se il contenuto di secret
non è mai "veramente" (cioè, non speculativamente) letto.
Dopodiché, accedo l'accesso alle varie linee e mi aspetto un tempo significativamente più breve per il line
determinato dal contenuto di secret
(ovvero, la lettura da A[256+'H']
dovrebbe essere più veloce dell'accesso ad altro A[256+c]
).
Tuttavia, misuro costantemente > 1000 cicli per tutti gli accessi di test, mentre mi aspetterei che < 100 cicli per accedere a una linea già memorizzata. (Potrei verificare il tempo più breve cambiando la condizione if
e rendendo l'accesso "reale" anziché solo speculativo).
Per me, sembra che la vulnerabilità di Spectre non esista affatto, cioè, il recupero della cache speculativa non si verifica !?
Ma l'ho provato in particolare su un sistema che non ha ancora aggiornato kernel / compilatore / BIOS / firmware / microcodice. Almeno questo è ciò che spectre-meltdown-checker.sh
sembra per confermare:
Spectre and Meltdown mitigation detection tool v0.29
Checking for vulnerabilities against running kernel Linux 4.4.0-104-generic #127-Ubuntu SMP Mon Dec 11 12:16:42 UTC 2017 x86_64
CPU is Intel(R) Celeron(R) CPU G530 @ 2.40GHz
CVE-2017-5753 [bounds check bypass] aka 'Spectre Variant 1'
* Checking count of LFENCE opcodes in kernel: NO
> STATUS: VULNERABLE (only 33 opcodes found, should be >= 70, heuristic to be improved when official patches become available)
CVE-2017-5715 [branch target injection] aka 'Spectre Variant 2'
* Mitigation 1
* Hardware (CPU microcode) support for mitigation: NO
* Kernel support for IBRS: NO
* IBRS enabled for Kernel space: NO
* IBRS enabled for User space: NO
* Mitigation 2
* Kernel compiled with retpoline option: NO
* Kernel compiled with a retpoline-aware compiler: NO
> STATUS: VULNERABLE (IBRS hardware + kernel support OR kernel with retpoline are needed to mitigate the vulnerability)
CVE-2017-5754 [rogue data cache load] aka 'Meltdown' aka 'Variant 3'
* Kernel supports Page Table Isolation (PTI): NO
* PTI enabled and active: NO
> STATUS: VULNERABLE (PTI is needed to mitigate the vulnerability)
A false sense of security is worse than no security at all, see --disclaimer
Quindi - ironia della sorte - quell'ultima riga di questo output sembra essere applicata al mio test interno: non riesce ad esporre la vulnerabilità anche se il sistema è vulnerabile. Q: Ma perché il mio test fallisce in questo modo? Cosa c'è di sbagliato nella mia argomentazione sopra "dimostrare" che dovrebbe esporre la vulnerabilità?
Nota: , naturalmente, ho compilato il mio codice senza ottimizzazione, quindi ti assicuro che l'assemblatore generato corrisponde alla struttura della sorgente (l'ho verificato con objdump
). Inoltre, quanto sopra è solo la versione più basilare del codice che ho provato; per "incoraggiare" la speculazione, ho reso il if
dipendente da un calcolo relativamente lungo e il then
-body consisteva in una singola istruzione (disassemblata come
imul -0x66c(%rbp),%eax
cmp %eax,%edx
je 4006d7 <main+0xf5>
movzbq (%rdi),%rdi
4006d7: ...