Quanto è importante al giorno d’oggi garantire che tutte le mie DLL abbiano indirizzi di base non conflittuali?
Raymond
20 gennaio, 2017
Indietro nel giorno, una delle cose che vi sono stati esortati a fare era rebase le Dll in modo che tutti avevano non sovrapposte intervalli di indirizzi, evitando in tal modo il costo di runtime di trasferimento. Questo è ancora importante al giorno d’oggi?
Questa situazione è un’altra dimostrazione di come sia importante che un buon consiglio venga fornito con una logica in modo da poter dire quando diventa un cattivo consiglio.
La logica per il rebasing è la seguente: se una DLL viene caricata al suo indirizzo di base preferito, l’immagine può essere impaginata direttamente dal backing store senza richiedere alcuna correzione. Ciò significa che le pagine possono essere condivise tra i processi, poiché ogni processo ottiene una copia identica. (Ovviamente, la condivisione si interrompe una volta che qualcuno scrive sulla pagina e rende la sua copia diversa dalla copia condivisa.)
Se una DLL non può essere caricata al suo indirizzo preferito, l’immagine verrà trasferita e l’intera DLL trasferita è ora supportata dal file di paging.1 Questa è un’operazione relativamente costosa, poiché la DLL deve essere letta dal disco e riparata, e viene addebitato un costo di commit sul file di pagina per garantire che ci sia spazio per scrivere le pagine fisse. Inoltre, se due processi rilocano la DLL e capita per qualche coincidenza di trasferirli nello stesso posto, Windows NT non tenta di condividere le immagini trasferite. Ci saranno più copie nel file di paging.
Il costo di questa delocalizzazione dinamica è ciò che rebasing tenta di evitare. Chiamiamola “penalità di trasferimento”.”
Inserisci ASLR.
ASLR fa sì che le DLL vengano caricate su indirizzi pseudo-casuali. Di conseguenza, una DLL verrà caricata al suo indirizzo di base preferito solo nel caso di una coincidenza sorprendente.
Va bene, quindi torniamo alla logica per vedere se si applica ancora.
Una DLL che viene caricata dal suo indirizzo di base preferito comporta una penalità di delocalizzazione? Se ci pensate, ASLR significa che nessuna DLL viene mai caricata al suo indirizzo preferito, ma abbiamo anche visto che il kernel fa degli adattamenti per questo in modo che le DLL sottoposte ad ASLR possano ancora condividere le pagine, e lo fa senza forzare l’intera DLL da ricollocare al carico iniziale. Quindi non vi è alcuna penalità di delocalizzazione nel caso in cui la DLL sia stata trasferita da ASLR.
Ma cosa succede se la DLL viene trasferita per qualche altro motivo? Ad esempio, potrebbe essere che l’indirizzo di base scelto da ASLR non sia disponibile nel processo, perché il processo ha già allocato qualcos’altro in quella posizione. In tal caso, un trasferimento tradizionale deve avvenire, e si paga la pena di trasferimento.
Ah, ma ecco la cosa: quando viene caricata una DLL, ASLR assegnerà un indirizzo di base in modo casuale tra gli indirizzi di base disponibili che non sono già in uso.2 Quindi non entrerai nello scenario” l’indirizzo di base scelto da ASLR non è disponibile ” perché ASLR sceglie l’indirizzo di base della DLL tra l’indirizzo di base disponibile.3
Va bene, quindi puoi ancora entrare in una situazione di conflitto, ma devi davvero lavorarci. Ad esempio, è possibile caricare una DLL in un processo e ottenere un indirizzo di base ASLR assegnato. Si avvia quindi un secondo processo, si assegna intenzionalmente memoria a quell’indirizzo (per forzare la collisione) e quindi si carica la DLL. In questo caso, ci sarà un trasferimento perché ti sei accovacciato nel luogo in cui ASLR voleva mettere la DLL. Ma questo non è peggiore di quello che avevi prima di ASLR: nel mondo pre-ASLR, accovacciarsi sull’indirizzo base preferito di una DLL avrebbe comunque forzato una penalità di delocalizzazione.
Quindi, vediamo qual è la storia. Per rebase o non per rebase?
In presenza di ASLR, il rebasing delle DLL non ha alcun effetto perché ASLR ignorerà comunque l’indirizzo di base e trasferirà la DLL in una posizione di sua scelta pseudo-casuale.
Badate bene, anche se rebasing non ha alcun effetto, non fa male neanche.
Se sei su un sistema senza ASLR (o perché precede ASLR, o perché ASLR è stato disabilitato per qualsiasi motivo), allora il rebasing aiuterà, per i motivi tradizionali.
Intendiamoci, i sistemi senza ASLR sono davvero difficili da trovare al giorno d’oggi, quindi il rebasing non fornisce alcun beneficio nella stragrande maggioranza dei casi. Ma in quella piccola percentuale di casi in cui non si dispone di ASLR, il rebasing aiuta.
Conclusione: non fa male rebase, per ogni evenienza, ma capire che il payoff sarà estremamente raro. Crea la tua DLL con /DYNAMICBASE
abilitato (e con /HIGHENTROPYVA
per buona misura) e lascia che ASLR faccia il lavoro di garantire che non si verifichi alcuna collisione con l’indirizzo di base. Questo coprirà praticamente tutti gli scenari del mondo reale. Se ti capita di cadere in uno dei rarissimi casi in cui ASLR non è disponibile, allora il tuo programma funzionerà ancora. Potrebbe funzionare un po ‘ più lentamente a causa della penalità delocalizzazione.
1 Più precisamente, tutte le pagine che contenevano correzioni vengono inserite nel file di paging. Abbiamo discusso di questo punto più fine l’ultima volta.
2 Va bene, c’è un terzo caso, che è dove ASLR ha semplicemente esaurito gli indirizzi di base. Ma ancora una volta, questo non è peggio di quello che avevi prima di ASLR: se finisci gli indirizzi di base, allora è ogni uomo per se stesso. Ogni volta che una nuova DLL viene caricata, il kernel deve scroccare per un pezzo abbastanza grande di spazio di indirizzo disponibile in cui caricare la DLL.
3 Di conseguenza, ASLR fa effettivamente un lavoro migliore per evitare le collisioni rispetto al rebasing manuale, poiché ASLR può visualizzare il sistema nel suo complesso, mentre il rebasing manuale richiede di conoscere tutte le DLL caricate nel processo e il coordinamento degli indirizzi di base tra più fornitori non è generalmente possibile.
Raymond Chen
Seguire
Leave a Reply