|
|||||||
|
|
|
![]() |
|
|
Strumenti |
|
|
#21 |
|
Member
Iscritto dal: Jun 2000
Città: Milano
Messaggi: 136
|
@xeal
non sono cascato in nessun tranello, infatti anche tu mi dai ragione: "In realtà, questo caso specifico non dovrebbe essere un grosso problema, nel senso che comunque, almeno in una fase iniziale, la lettura dei parametri dovrebbe essere effettuata dal sistema operativo, che difficilmente la gestirà male " Io parlavo del caso specifico cioè quella main con solo return 0 è innocua. Non parlo del problema Buffer Overflow che è stra conosciuto e stra documentato. Ripeto quella funzione non è bucabile. |
|
|
|
|
|
#22 | ||
|
Senior Member
Iscritto dal: Jan 2002
Città: non ti interessa
Messaggi: 5649
|
Quote:
Per carità, se chi ha fatto il compilatore con cognizione fa tutto subito e senza problemi, ma se uno pretende di copiare una stringa da 200 byte in un buffer statico da 128... Quote:
Come ha detto xeal, nella realtà difficilmente c'è una vulnerabilità nel caso specifico, ma quel codice compilato da un compilatore fallato *potrebbe* essere vulnerabile. Il discorso è che *in potenza* anche quelle due righe di codice possono essere vulnerabili. Ultima modifica di blackshard : 05-06-2007 alle 19:21. |
||
|
|
|
|
|
#23 |
|
Senior Member
Iscritto dal: Jun 2003
Città: vivo in Sicilia (tra la prov. di AG e Palermo)
Messaggi: 956
|
Se è per questo, anche la funzione più bacata dell'universo finchè non la compili e non la esegui è innocua
E appena la compili saltano fuori un paio di cose: a) da sola non serve a niente, non può essere eseguita, quindi il compilatore dovrà aggiungere dei pezzi che possono essere "un po' meno innocui". Detto altrimenti, non è molto utile guardare alla singola istruzione, o a gruppi di istruzioni (puoi sostituire "return 0" con qualsiasi sequenza di codice esente da bug), o a singole funzioni, e dire questo è giusto è questo invece no, perchè non è questione di stabilire dove stia l'errore, quasi come se si dovesse trovare l'istruzione "colpevole", ma piuttosto bisogna considerare cosa accade quando tutti i pezzi sono messi insieme, perchè cisacuno, preso singolarmente, non serve a niente, ma quando sono messi insieme devono funzionare correttamente. In questo senso, anche una funzione buggata, presa singolarmente, può essere "corretta", nella misura in cui fa esattamente quello che il programmatore voleva fare con la sequenza di input/output che aveva in mente: quando si programma qualcosa, a meno di avere una certa esperienza ed essere abituati a prevedere l'imprevedibile (e se programmi con i criteri del test driven developement ci riesci meglio), avrai comunque in mente una situazione specifica da riprodurre (nel nostro esempio, il non fare niente), e finchè si presenta quella precisa situazione (nel nostro esempio, possiamo pensare all'esecuzione del programma senza passare argomenti) non ci saranno problemi. I problemi però possono nascere nel momento in cui alla funzione vengono passati dei dati non previsti dal programmatore, ed ecco che può succedere qualcosa di sgradevole: seguendo la logica del "questo è innocuo" verrebbe da pensare che il problema risiede altrove, perchè da qualche altra parte sono stati preparati dei dati "sbagliati" da dare in pasto alla mia funzione, però la mia funzione non deve creare problemi anche quando elabora dei dati "sbagliati", quindi se in presenza di errori "esterni" va in crisi è comunque buggata anche la mia funzione. b) Tu stai considerando in pratica solo quello che sta dentro le parentesi graffe, e cioè "return 0", e concludi che è innocuo, però la funzione semplicissima su cui stiamo discutendo fa anche altre cose, solo che non si vedono subito perchè le fa implicitamente: non mi riferisco solo a tutto ciò che sta attorno e che serve a creare un processo completo, ma alla funzione in sè, perchè nel dichiarare "int main(int argc, char **argv)" io sto dicendo che la funzione ha due variabili locali, argc e argv, e che queste variabili vanno inizializzate (formalmente le chiamiamo "parametri formali", ma in pratica sono delle vere e proprie variabili locali che vengono inizializzate scrivendoci qualcosa che viene preso altrove, quando cioè avviene la sostituzione dei "parametri attuali" ), ed è proprio questa inizializzazione, che comunque fa parte della funzione, che può creare problemi (anche se viene gestita, o può esserlo, in parte, da qualcosa di esterno: diciamo che il problema è a cavallo tra la funzione e il codice che la chiama, quindi il problema è di entrambi). Se volessimo scrivere quella funzione in pseudocodice, guardando solo ciò che sta dentro, scriveremmo: 1. esci senza fare niente * *in realtà fa qualcosa, ma ne parliamo tra un po' quando invece l'intera funzione corrisponde a: 1. inizializza il primo parametro 2. inizializza il secondo parametro 3. esci senza fare niente A questo punto si potrebbe obiettare: ma argc e argv sono rispettivamente un intero e un puntatore, quindi è praticamente impossibile che vengano sovrascritti con valori sbagliati. Soffermiamoci sul "praticamente": questo tipo di dati hanno una dimensione fissa e sono facili da gestire correttamente (anche se questo non esclude, in teoria, un errore), ma la suddetta dimensione, in linguaggi come il C, dipende dalla macchina, quindi un ipotetico compilatore bacato, che consenta di produrre codice per macchine diverse, può IN TEORIA fare dei pasticci, e questo in risposta ad un nostro preciso ordine espresso da "int main(int argc, char** argv)". L'errore può avvenire ad opera del compilatore, ma anche del programmatore, ad esempio si potrebbe prevedere una direttiva per indicare il tipo di processore per cui si compila, per poi indicare male il codice del processore in una define (dove per male si intende o l'indicazione del codice sbagliato, oppure del codice giusto ma in un posto sbagliato, o di codici diversi in più posti, creando così confusione); in ogni caso, il risultato finale non cambia: TEORICAMENTE si rischia di sovrascrivere qualcosa che non si dovrebbe invece toccare (potrebbe anche accadere qualcosa di più subdolo, benchè forse meno probabile: un errore hw momentaneo corrompe alcuni dati in memoria e tra questi viene alterato il contenuto di una variabile usata dal compilatore per determinare la macchina target). E a questo punto diventa molto meno innocuo anche il semplice "return 0", perchè non dice che la funzione deve terminare e basta, ma che deve ritornare il valore 0, il quale sarà scritto da qualche parte, quindi ci ritroviamo di nuovo con il rischio di un problema a cavallo tra quello che fa il nostro codice e quello che fa il sistema operativo, o più in generale il codice che sta attorno. E, sempre nel caso generale, gli eventuali errori da chi vanno gestiti/tenuti in conto in fase di programmazione, dal codice che faccio io o da quello che sta attorno e che fai tu? nel dubbio: da tutti e due. Chiaramente, stiamo discutendo su di una situazione un po' estrema, però il fatto che si possa verificare con estrema difficoltà (nello specifico) non vuol dire che il rischio, ad un livello teorico, non sussista, anzi. Ma allora come faccio a sapere se il mio codice è "innocuo" oppure no? Semplice: NON POSSO! Non a priori, almeno. Quindi dovrò fare dei test approfonditi, nel limite dell'umanamente possibile, cercando di tener conto anche di certe sottigliezze, ma senza esagerare, perchà altrimenti mille anni non mi basterebbero... e allora mi fermo ad un livello ragionevole, confidando che certe questioni "basilari" vengano trattate adeguatamente ad un livello più basso di quella a cui opero io (= compilatore, sistema operativo, ecc.). In questo senso, quel semplicissimo main che "non fa niente" (ma abbiamo visto che non è vero: copia due valori esterni nelle sue variabili interne e copia il valore di ritorno in una variabile esterna) si può considerare ragionevolmente sicuro, ma non si può affermare che sia sicuramente esente da "difetti" Chiudendo con un cliché: L'unico computer sicuro, è un computer spento!
|
|
|
|
|
| Strumenti | |
|
|
Tutti gli orari sono GMT +1. Ora sono le: 06:15.




















