Cos'è Stack Smashing? Può essere risolto?

0
218
Shutterstock/Gabor Tinz

Ogni minuto di fermo della produzione generalmente costa denaro a un'azienda. Se la tua applicazione ha un problema serio che causa lo stack smashing, sei pronto per un giro. Scopri in anticipo cos'è lo stack smashing e cosa si può fare al riguardo!

Cos'è Stack Smashing?

< p>Lavorando come ingegnere di garanzia della qualità, prima o poi ci si imbatte nel termine rottura di pile. Come sviluppatore, probabilmente scoprirai questo termine anche prima, specialmente se hai introdotto un bug nel codice, che causa uno stack rotto. È relativamente facile (come in ‘un po' facile’) per uno sviluppatore commettere un errore che introduce lo stack smashing. Come utente, quando impari a distruggere lo stack, è probabile che il danno sia già stato fatto.

Lo stack smashing può verificarsi involontariamente – ad esempio, quando lo sviluppatore ha introdotto un bug che ha causato lo stack smashing – o maliziosamente – un utente malintenzionato che cerca in qualche modo di traboccare o corrompere lo stack di un programma.

Smack dello stack è un termine un po' genericamente definito che può indicare vari problemi e può provenire da una varietà di fonti. I due problemi più importanti che possono causare lo stack smashing sono; 1) per scrivere/sovraallocare troppi dati in una data parte dello stack, sovrascrivendo così un'altra parte dello stack, e 2) dove una fonte esterna (dannosa o meno) ha sovrascritto lo stack di un altro programma, sebbene questo è molto meno comune.

Quindi cos'è una pila? Anche questo è un termine vagamente definito. In generale, uno stack si riferisce a uno stack di elaborazione del programma, uno stack di funzioni come definito in un determinato programma/codice software.

Pubblicità

Inizia immaginando una pila di piastrelle del bagno impilate, pronte per essere utilizzato da un piastrellista. Questa è una buona rappresentazione di uno stack di computer, con alcune modifiche. Se ogni tessera fosse leggermente sfalsata rispetto alla precedente, sarebbe un'immagine migliore e presto vedremo perché.

Shutterstock/Piotr Debowski

Immagina che ogni tessera impilata sia una funzione nel programma del computer. La funzione più elementare si trova in basso e potrebbe essere ad esempio la funzione main() in un programma C o C++. C e C++ sono due linguaggi di programmazione che utilizzano ampiamente lo stack.

Ognuna di queste funzioni nel programma C/C++ avrà un nome e probabilmente un insieme di variabili in entrata e in uscita. In termini semplificati, immagina se una di queste variabili ha una lunghezza di 10 caratteri e qualche altra funzione ha scritto accidentalmente 100 caratteri su quella variabile. Questo potrebbe corrompere l'intera pila.

In termini di esempio di tessere sopra, immagina che qualcuno con un martello colpisca la prima tessera un po' troppo forte e quindi distrugga tutte le altre tessere. Eh voilà; spacca pila 😉

L'analogia funziona perché, proprio come tutte le tessere sono ora rotte nella nostra immagine di memoria fittizia, una pila rotta si tradurrà in ‘funzioni interrotte’ se lo desideri. Ogni offset di riquadro è una funzione annidata più in profondità – maggiori informazioni sulle funzioni danneggiate nella prossima sezione.

Debug di Smashed Stack(s)

Mentre tecnicamente un riferimento a ‘funzioni interrotte’potrebbe non essere completamente corretto, ad esempio, è probabile che ci sia solo una funzione danneggiata e potrebbe anche non esserci alcuna funzione interrotta quando c'è un attacco esterno o un programma malfunzionante, è un ottimo modo per pensare a uno stack distrutto.

Pubblicità

Improvvisamente, i nomi delle variabili e delle funzioni possono essere alterati e un backtrace (il flusso di funzioni utilizzato dal computer per arrivare a una determinata funzione che si è arrestata in modo anomalo e (nel nostro esempio) ha distrutto lo stack) non ha senso più.

In generale, quando osserviamo un backtrace, avrà un chiaro flusso di funzioni che sono state chiamate. Anche se un programma che va in crash non può essere chiamato immediatamente ‘sano,’ in termini di backtracing/debug, questo è ciò che un ‘sano’ backtrace assomiglia a:

Quando uno stack è danneggiato, tuttavia , il debug diventa molto più difficile. Lo stack potrebbe essere simile a questo:

Questo è un esempio di problema di stack smashing che si è verificato in MySQL, il server di database (vedi l'allegato log.txt a MySQL Bug 37815 per l'output completo) nel 2008, causando la chiusura del demone del server di database (mysqld).

< p>Mentre la libreria del sistema operativo libc.so.6, in questo caso, sembra aver gestito abbastanza bene lo stack smashing (usando alcune funzionalità di fortificazione nella funzione __fortify_fail), il problema esisteva da qualche parte nel codice e ha da allora è stato corretto.

Nota anche che in questo caso, non vediamo i nomi delle funzioni risolti, ci viene mostrato solo il nome binario (interessante, il problema sembra essere stato nel client (mysql) causando la terminazione del server (mysqld) che è mysql, insieme a un indirizzo di memoria della funzione: mysql[0x8051565], mysql[0x80525c7] e mysql(main+0x4f8)[0x8053198].

Annuncio

Normalmente, quando usiamo i simboli di debug (vedi sotto per un articolo su GDB che spiega cosa sono i simboli di debug in dettaglio), vedremmo nomi di funzioni con variabili, e anche con alcuni livelli di ottimizzazione/minificazione binaria in atto, almeno vedere i nomi delle funzioni, proprio come quello che vediamo nel primo ‘sano’ backtrace sopra.

Tuttavia, nel caso di uno stack distrutto, l'output dei nomi delle funzioni, dei nomi delle variabili o dei valori non è mai garantito e spesso completa mumbo-jumbo 🙂 Potremmo persino vedere nomi di funzioni diversi o un stack completamente alterato (un altro gergo spesso usato dagli esperti di IT) di nomi di funzioni differenti che non hanno molto senso (e sono probabilmente fittizi/falsi poiché lo stack è stato in qualche modo sovrascritto). >Ciò rende più difficile sia l'ingegnere del test (che potrebbe finire con molti risultati diversi per un singolo bug, complicando la gestione del meccanismo di filtraggio dei bug noto) sia lo sviluppatore (che probabilmente dovrà utilizzare una traccia passo-passo o un debugger di esecuzione inversa come RR per scoprire il bug a portata di mano).

Cosa fare quando si affronta lo Stack Smashing?

Se ti imbatti in uno stack smashing, la prima cosa che vuoi fare è capire un po' meglio il problema e l'ambiente per conoscerne la fonte. Se hai un server web popolare esposto su Internet con molti utenti di gioco che stanno cercando di vincere un torneo mentre il server sta anche estraendo Bitcoin, vorrai presumere la possibilità di un gioco scorretto e capire se qualcuno sta scherzando con il server.

Tuttavia, nella maggior parte dei casi, il problema sarà solo un errore dell'applicazione. Mentre dico ‘solo’, il problema potrebbe essere molto significativo, potrebbe causare tempi di inattività dei servizi, potrebbe costare molto denaro e infine potrebbe non essere possibile risolverlo. Ad esempio, un server di database potrebbe bloccarsi in modo permanente quando viene avviato a causa del fatto che i dati si trovano in un determinato stato in combinazione con un difetto o una limitazione nel codice.

Se una situazione del genere è aggravata dal mancato stack smashing o, in altre parole, dall'impossibilità di generare un backtrace pulito del problema, il debug sarà più complicato ea volte quasi impossibile. Non temere, tuttavia, lo stesso debug di base di qualsiasi bug o errore/crash/problema dell'applicazione rimane lo stesso.

Annuncio

Leggi attentamente ogni bit dei file di registro prima, durante e dopo il problema si è verificato. Eseguire alcuni backup e riprovare l'operazione. Fallisce di nuovo o no? La ricerca degli errori, delle parti dello stack e persino dei frame (ovvero, le singole funzioni dello stack mostrate, come la funzione do_the_maths nel nostro ‘sano’ stack trace originale) possono essere inseriti nei tuoi motori di ricerca preferiti.

La concatenazione (con uno spazio) dei frame di arresto anomalo più selettivi (in alto) e la ricerca degli stessi online spesso ti trova una segnalazione di bug esistente per il problema che stai affrontando. Tuttavia, nel caso dello stack smashing, è probabile che questi frame (nomi di funzioni) siano stati alterati e quindi non siano più utilizzabili allo stesso modo. Se vedi un messaggio di asserzione (un'asserzione istituita da uno sviluppatore nel codice) di qualsiasi tipo, cerca anche quello.

Registra sempre una nuova segnalazione di bug se il problema non sembra essere stato ancora registrato online (tu potrebbe aiutare altri che vedono lo stesso!) e fornire quante più informazioni sul problema che puoi trovare. Migliaia di segnalazioni di bug su altrettante applicazioni vengono registrate online ogni giorno. Si spera che il team di supporto per la tua applicazione di distruzione dello stack sia a disposizione per aiutarti rapidamente.

Potrebbe anche interessarti leggere il nostro articolo Debugging con GDB: Getting Started, poiché si basa ulteriormente su come i programmi C e C++ (e altri) possono essere sottoposti a debug con il debugger GDB. Spiega inoltre ulteriormente i concetti di stack in dettaglio.