2010-03-30 8 views
8

Uno stack è un blocco contiguo di memoria contenente dati . Un registro ha chiamato i punti dello stack pointer (SP) in cima allo stack. Il fondo dello stack è a un indirizzo fisso.Come viene determinato il fondo della pila?

Come è lo stack in basso fissato dal kernel?

risposta

1

Questo sarebbe parte dell'implementazione dello stack stesso. Se stai implementando uno stack in (ad esempio) C, puoi memorizzare il puntatore dello stack e il numero corrente di elementi. O lo stack pointer insieme alla base della pila, qualcosa come:

typedef struct { 
    int *sp_empty; 
    int *sp; 
    int *sp_full; 
} tIntStack; 

tIntStack stk; 

// Initialise 20-element stack. 
stk.sp = stk.sp_empty = malloc (sizeof(int) * 20); 
stk.sp_full = &(stack[20]); 

// Push a value x, detecting overflow.  
if (stk.sp == stk.sp_full) { error here} 
*(stk.sp) = x; 
stk.sp++; 

// Pop a value x, detecting underflow.  
if (stk.sp == stk.sp_empty) { error here} 
stk.sp--; 
x = *(stk.sp); 

Se si sta parlando di uno stack CPU (indirizzi di ritorno e così via), è possibile rilevare l'underflow dello stack in virtù del fatto che ti schianti. Male.


Ad esempio, in tempi antichi quando processori sono limitati a 64 KB spazio di indirizzi, CPU memoria tipicamente testato fino a trovare il primo byte che non ha letto lo stesso di quello che è stato appena scritto (in avvio processi). Il primo era oltre la reale memoria fisica, quindi impostarono SP su uno sotto. Naturalmente, alcune (poche) macchine avevano 64K di RAM fisica, quindi basta impostare SP all'inizio dello spazio degli indirizzi.

Il processo al giorno d'oggi, in enorme-indirizzo-spazio, memoria virtuale, multi-tasking sistemi operativi, è un po 'più complicato, ma si riduce ancora giù a (nella maggior parte dei casi): spazio

  • Indirizzo è assegnato per un processo.
  • SP è impostato in qualche punto dello spazio indirizzo.
  • Esecuzioni del processo.

A quel punto, probabilmente siete da soli per quanto riguarda il kernel. La sua responsabilità si arresta nel punto in cui il codice inizia a funzionare diverso dal cambio di attività e fornisce i servizi richiesti, ma questo non ha nulla a che fare con lo stack. Se il tuo codice buggy è in overflow o sottobocca lo stack, questo è il tuo problema.

Il kernel può controllare il SP sul task switch per vedere se hai fatto qualcosa di sbagliato, ma questo non è affatto garantito. Può anche utilizzare la protezione della memoria hardware per rilevare un underflow (se lo stack si trova nella parte superiore dello spazio di indirizzi allocato). Ma ancora, questo dipende interamente dal kernel che stai usando.

+0

Che cosa intendi per "underflow", ho solo sentito parlare di "overflow". – Mask

+0

L'underflow si ha quando si tenta di estrarre un elemento da una pila vuota. Nella maggior parte dei sistemi, comporterà l'accesso a un indirizzo di memoria non mappato, causando un errore di segmentazione. –

+0

Perché è necessario un errore di segmentazione invece di restituire solo un valore 'false'? – Mask

6

ho digitato una risposta più lungo, ma divagò, quindi ecco uno più corto ...

Quando viene avviato un processo, esso ha bisogno di una pila a scopo di memorizzare valori temporanei (cioè allocazione automatica) o impila i frame quando vengono chiamate le funzioni. La memoria per questa pila deve venire da qualche parte.

Quindi il sistema operativo crea una mappatura nella memoria virtuale per lo stack e assegna il puntatore allo stack all'indirizzo alto di questo blocco.In uno stack di predecrement, in cui il puntatore dello stack viene decrementato prima del dereferenziamento, il puntatore dello stack iniziale è in realtà l'indirizzo oltre l'ultimo indirizzo nello spazio mappato.

Quando il processo tenta di mettere qualcosa nello stack, si ottiene un accesso a questa area di memoria, che non ha alcuna RAM fisica associata ad esso. Ciò provoca un errore di pagina, che porta il sistema operativo a rilasciare la pagina di RAM meno utilizzata (o vicina ad esso) nell'unità di scambio o nel file di paging e a riassegnare la pagina RAM fisica alla pagina dello stack a cui si accede. Ora che c'è la RAM fisica, il sistema operativo ritorna e il processo continua, inserendo i dati spinti nella memoria dello stack.

Quindi cosa succede se si esclude tutto dallo stack e quindi si tenta di eseguire nuovamente il pop? L'ultimo pop, in cui il puntatore dello stack è al suo valore iniziale, consente di accedere a un indirizzo di memoria virtuale che è non mappato allo stack. Ciò provoca un errore di segmentazione, il che significa che il processo ha tentato di accedere alla memoria che non ha mai assegnato. Il sistema operativo risponde chiudendo il processo e ripulendo tutto ciò che può.

Perché non mappare una pagina oltre la fine della pila? perché ciò risulterebbe nella lettura della RAM non inizializzata, che conterrà qualsiasi cosa usata per utilizzare quella pagina di RAM fisica. C'è semplicemente in nessun modo questo può produrre un programma correttamente funzionante (per non parlare di come questo è un enorme rischio per la sicurezza), quindi è ancora meglio uccidere il programma.

+0

Bella risposta. Mi piacerebbe vedere cosa sia la versione * lunga *. :-) –

+0

la memoria virtuale è essenzialmente un disco rigido, che è molto lento, quindi penso che sia usato solo quando la RAM non è abbastanza, giusto? – Mask

+0

@Mask, la memoria virtuale è * non * solo disco rigido. È una disconnessione tra lo spazio degli indirizzi che il processo vede e la memoria fisica effettiva.È possibile avere memoria virtuale senza swapping o paging su disco, nel qual caso non è necessario preoccuparsi del codice indipendente dalla posizione - si presume che il proprio codice verrà eseguito con lo stesso indirizzo di memoria indipendentemente da dove si trovi effettivamente nella memoria fisica. Potresti anche avere il paging senza memoria virtuale se più processi volevano utilizzare gli stessi indirizzi. – paxdiablo

0

Suppongo che intendiate "fisso nello spazio di memoria del processo" e non "fissato nella memoria generale". Potete guardare dove il fondo stack è in alcun recente sistema Linux attraverso la ricerca di una linea come

bfbce000-bfbe3000 rw-p bffeb000 00:00 0   [stack] 

nell'output del cat /proc/<pid-here>/maps. Il fondo dello stack è, in questo caso, a 0xbffeb000. Nel mio sistema, tutti i fondi dello stack sembrano rientrare in uno dei bffca000 bffcb000 bffdd000 bffe0000 bffe4000 bffe6000 bffeb000 (dopo aver eseguito il ciclo di ~ 200 processi).

Immagino che questi valori siano assegnati in profondità nel kernel, ovunque vengano creati i processi.

+0

Penso che gli indirizzi di stack siano randomizzati alcuni per rendere più difficile il funzionamento degli exploit di overflow dello stack. –

+0

@Mike Ricordo una sorta di randomizzazione in corso in indirizzi mappati in memoria, ma se gli stack sono effettivamente randomizzati, allora 7 valori da 200 campioni sono davvero una scarsa randomizzazione :-) – tucuxi

+0

Ora che ne parli, sembra più come qualcos'altro che viene mappato nello spazio più vicino a c0000000 e che ha una dimensione incoerente (54, 53, 35, 32, 28, 26 o 21 pagine 4K). –

0

In realtà, c'è qualcosa dopo il fondo della pila. Dopo lo stack, ci sono gli elementi dell'array argv, quindi gli elementi dell'array env. Di seguito c'è una barriera, quindi codice di libc.

Problemi correlati