2014-08-20 26 views
15

Si consiglia vivamente di creare un kernel a 64 bit (per piattaforma x86_64), per indicare al compilatore di non utilizzare la zona rossa a 128 byte che l'ABI dello spazio utente esegue . (Per GCC il flag del compilatore è -mno-red-zone).Perché il codice del kernel non può utilizzare una zona rossa

Il kernel non sarebbe protetto da interruzioni se abilitato.

Ma perché?

+1

Correlati: http://stackoverflow.com/questions/38042188/where-exactly-is-the-red-zone-on-x86-64 e http://stackoverflow.com/questions/37941779/why-do -e-abbiamo-bisogno-stack-allocazione-quando-abbiamo-una-zona-rossa hanno risposte che spiegano cos'è la zona rossa per il codice che può usarlo. –

risposta

11

Citando dalla AMD64 ABI:

La zona 128 byte oltre la posizione indicata dal% RSP è considerato essere riservati e non è modificata dal segnale o interrompere gestori. Pertanto, le funzioni possono utilizzare quest'area per i dati temporanei che non sono necessari per le chiamate di funzione. In particolare, le funzioni foglia possono usare quest'area per l'intero frame dello stack, piuttosto che regolare il puntatore dello stack nel prologo e nell'epilogo. Questa zona è conosciuta come la zona rossa.

In sostanza, si tratta di un'ottimizzazione - il compilatore userland sa esattamente quanto della zona rossa viene utilizzato in un dato momento (nel più semplice implementazione, l'intera dimensione delle variabili locali) e in grado di regolare la %rsp di conseguenza prima di chiamare una sotto funzione.

Soprattutto nelle funzioni foglia, questo può comportare alcuni vantaggi in termini di prestazioni di non dover regolare %rsp poiché è possibile che nessun codice non familiare venga eseguito durante la funzione. (I gestori di segnale POSIX possono essere visti come una forma di co-routine, ma è possibile istruire il compilatore a regolare i registri prima di utilizzare le variabili di stack in un gestore di segnale).

Nello spazio del kernel, una volta che si inizia a pensare agli interrupt, se tali interruzioni fanno supposizioni su %rsp, probabilmente non saranno corretti - non c'è certezza in merito all'utilizzo della Zona Rossa. Quindi, si presume che tutto sia sporco, e inutilmente sprechi spazio nello stack (efficacemente in esecuzione con una variabile locale garantita di 128 byte in ogni funzione), oppure, si garantisce che gli interrupt non facciano ipotesi su %rsp - il che è complicato.

Nello spazio utente, gli interruttori di contesto + sovrassegnazione di stack a 128 byte lo gestiscono per voi.

+5

Non è solo salvaspazio. In realtà è impossibile implementare la normale zona rossa a 128 byte in modo sicuro, poiché gli interrupt sempre comprimono i 16 byte sotto '% rsp' prima che venga eseguito qualsiasi codice dal gestore di interrupt. –

+0

@qdot, potresti spiegare cosa intendi per sovrassegnazione a 128 byte? Significa che se l'ABD di amd64 non aveva il concetto di "zona rossa", lo stack di indirizzi più basso potrebbe crescere più di 128 byte in più? –

8

Nello spazio del kernel, si sta utilizzando lo stesso stack che interrompe l'utilizzo. Quando si verifica un'interruzione, the CPU pushes a return address and RFLAGS. Ciò elimina 16 byte al di sotto di rsp. Anche se volessi scrivere un gestore di interrupt che presupponesse che i 128 byte completi della zona rossa fossero preziosi, sarebbe impossibile.


Si potrebbe forse avere un ABI kernel interna che aveva una piccola zona rossa rsp-16-rsp-48 o qualcosa del genere. (Piccolo perché lo stack del kernel è prezioso e la maggior parte delle funzioni non richiede comunque molto la zona rossa.)

Gli operatori di interrupt dovrebbero sub rsp, 32 prima di premere qualsiasi registro. (e ripristinarlo prima di iret).

Questa idea non funzionerà se un gestore di interrupt può essa stessa essere interrotta prima che si sub rsp, 32, o dopo che ripristina rsp prima di un iret. Ci sarebbe una finestra di vulnerabilità in cui i dati importanti sono a rsp .. rsp-16.


Un altro problema pratico con questo schema è che AFAIK gcc non ha parametri di zona rossa configurabili. È acceso o spento. Quindi dovresti aggiungere il supporto per l'aroma del kernel di red-zone a gcc/clang se volessi approfittarne.

Anche se è sicuro da interruzioni nidificate, i vantaggi sono piuttosto ridotti. La difficoltà di dimostrare che è sicuro in un kernel potrebbe renderlo non ne vale la pena. (E come ho detto, io non sono affatto sicuro che può essere implementato in modo sicuro, perché penso che gli interrupt annidati sono possibili.)


(A proposito, vedere il wiki tag per collegamenti alla ABI documentando la zona rossa e altre cose)

1

È possibile utilizzare la zona rossa in contesti di tipo kernel. IDTentry può specificare un indice stack (ist) di 0..7, dove 0 è un po 'speciale. Il TSS contiene una tabella di questi stack. 1..7 vengono caricati e utilizzati per i registri iniziali salvati dall'eccezione/interrupt e non vengono nidificati. Se partizionate le varie voci di eccezione per priorità (ad esempio NMI è il più alto e può accadere in qualsiasi momento) e trattate questi stack come trampolini, potete tranquillamente gestire zone rosse in contesti di tipo kernel. Cioè, è possibile sottrarre 128 dal puntatore dello stack salvato per ottenere uno stack del kernel utilizzabile prima di attivare interrupt o codice che possono causare eccezioni.

Lo stack di indice zero si comporta in modo più convenzionale, spingendo stack, flag, pc, errore nello stack esistente quando non è presente alcuna transizione di privilegi.

Il codice nel trampolino deve fare attenzione (duh, è un kernel) a non generare altre eccezioni mentre disinfetta lo stato della macchina, ma fornisce un punto bello e sicuro per rilevare il nidificazione del kernel patologico, il danneggiamento dello stack, ecc. ... [mi dispiace di rispondere così tardi, ho notato questo mentre cercavo qualcos'altro].