2010-06-04 10 views
5

Da man gets:se uno si lamenta di gets(), perché non fare lo stesso con scanf ("% s", ...)?

Non utilizzare mai gets(). Perché è impossibile dire senza conoscere i dati in anticipo quanti caratteri gets() leggerà, e perché gets() continuerà a memorizzare caratteri oltre la fine del buffer, è estremamente pericoloso usare . È stato utilizzato per interrompere la sicurezza del computer . Usa invece fgets().

Quasi ovunque vedo scanf in uso in un modo che dovrebbe avere lo stesso problema (buffer overflow/buffer overrun): scanf("%s",string). Questo problema esiste in questo caso? Perché non ci sono riferimenti a riguardo nella pagina man scanf? Perché gcc non avvisa quando si compila questo con -Wall?

ps: so che c'è un modo per specificare nella stringa di formato la lunghezza massima della stringa con scanf:

char str[10]; 
scanf("%9s",str); 

edit: io non chiedo di Determe se il codice precedente è giusto o non. La mia domanda è: se scanf("%s",string) è sempre sbagliato, perché non ci sono avvisi e non c'è nulla al riguardo nella pagina man?

+1

Il tuo link wikipedia dice 'scanf' è * non sicuro *. – aviraldg

+0

@aviraldg Hai ragione e l'ho letto prima, ma non stavo trovando un bel modo per scrivere il titolo. L'ho modificato – dbarbosa

+0

Come tutti hanno risposto: 'scanf ("% s ", ...)' non è sicuro. Un altro riferimento a questo proposito: http://c-faq.com/stdio/scanfprobs.html. Ancora non capisco come non ci sia nulla in questa pagina man. – dbarbosa

risposta

5

La risposta è semplicemente che nessuno ha scritto il codice in GCC per produrre tale avviso.

Come si fa notare, un avviso per il caso specifico di "%s" (senza larghezza di campo) è appropriato.

Tuttavia, tenere presente che questo è solo il caso per il caso di scanf(), vscanf(), fscanf() e vfscanf(). Questo identificatore di formato può essere perfettamente sicuro con sscanf() e vsscanf(), quindi l'avviso non dovrebbe essere emesso in quel caso. Ciò significa che non puoi semplicemente aggiungerlo al codice di analisi "scanf-style-format-string" esistente; dovrai dividerlo in opzioni "fscanf-style-format-string" e "sscanf-style-format-string".

Sono sicuro che se si produce una patch per l'ultima versione di GCC ha buone probabilità di essere accettata (e, naturalmente, sarà necessario inviare patch anche per i file di intestazione glibc).

+0

Perché dici che può essere perfettamente sicuro con 'sscanf()' e 'vsscanf()'? È perché è possibile controllare la stringa originale (il primo argomento) da cui sta leggendo scanf e accertarsi che sia adatto?(ad esempio, se entrambe le dimensioni sono uguali, se si cercano posizioni spaziali, ecc.) – dbarbosa

+0

Sì, esattamente. È * possibile * usarlo in sicurezza, dal momento che controlli l'input. – caf

4

L'utilizzo di gets() non è mai sicuro. scanf() può essere usato in modo sicuro, come hai detto nella tua domanda. Tuttavia, determinare se lo stai usando in modo sicuro è un problema più difficile da risolvere (ad esempio se stai chiamando scanf() in una funzione in cui passi il buffer e un numero di caratteri come argomenti, non sarà capace di dire); in tal caso, deve presumere di sapere cosa stai facendo.

+0

Sì, hai ragione, ma gcc ti avvisa quando il numero o il tipo di argomenti non è corretto. In questi casi, non presume che tu sappia cosa stai facendo. – dbarbosa

+0

@dbarbosa: tutte le informazioni sono disponibili per il compilatore per verificare che ci sia lo stesso numero di argomenti facoltativi quanti sono gli specificatori di formato nella stringa di formato. Allo stesso modo, ha anche informazioni sufficienti per verificare che se c'è un '% d' nella stringa di formato (ad esempio) che l'argomento corrispondente è un numero intero. –

+0

Non può sapere se la dimensione specificata è giusta, ma può sapere che è semplicemente sbagliata quando non ha alcuna specifica di dimensione. 'scanf ("% 5s ", string)' può essere giusto o sbagliato a seconda delle dimensioni di 'string' e non è in grado di dirlo come hai detto tu. Tuttavia, 'scanf ("% s ", stringa)' è sempre errato a causa del problema di overflow del buffer. – dbarbosa

-4

Potrebbe essere semplicemente che scanf allocherà spazio sull'heap in base alla quantità di dati letti. Poiché non alloca il buffer e quindi legge finché non viene letto il carattere null, non rischia di sovrascrivere il buffer. Invece, legge nel proprio buffer fino a quando viene trovato il carattere null e presumibilmente copia quel buffer in un altro della dimensione corretta alla fine della lettura.

+3

No; scanf non alloca spazio sull'heap. È compito del programmatore fornire i buffer. Scanf usato come diceva Dbarbosa non è sicuro. –

+0

Dai un'occhiata a questo tutorial, che spiega ciò che ho appena detto in modo più dettagliato e con maggiore precisione. http://crasseux.com/books/ctutorial/String-overflows-with-scanf.html – Graham

+0

'scanf' non alloca la propria memoria. il flag 'a' che hai collegato è un'estensione GNU, non parte dello standard C. –

3

Quando il compilatore esamina la stringa di formattazione di scanf, vede una stringa! Questo presuppone che la stringa di formattazione non venga inserita in fase di esecuzione. Alcuni compilatori come GCC hanno alcune funzionalità extra per analizzare la stringa di formattazione se inseriti in fase di compilazione. Questa funzionalità aggiuntiva non è completa, poiché in alcune situazioni è necessario un sovraccarico di run-time NO NO per le lingue come C. Ad esempio, puoi rilevare un utilizzo non sicuro senza inserire alcun codice nascosto aggiuntivo in questo caso:

char* str; 
size_t size; 
scanf("%z", &size); 
str = malloc(size); 
scanf("%9s"); // how can the compiler determine if this is a safe call?! 

Ovviamente esistono modi per scrivere codice sicuro con scanf se si specifica il numero di caratteri da leggere e che è presente memoria sufficiente per contenere la stringa. Nel caso di gets, non è possibile specificare il numero di caratteri da leggere.

+1

È molto difficile determinare se si tratta di una chiamata sicura, ma è facile vedere un 'scanf ("% s ", str)' e avvisare l'utente, ancor più che controllare che lo faccia già con il numero e il tipo di argomenti passati a 'scanf'. (a proposito, gcc dirà 'warning: troppi pochi argomenti per il formato' per il tuo codice perché non ci sono argomenti per il'% 9s' nel formato). – dbarbosa

Problemi correlati