Quali funzioni della libreria standard devono (dovrebbero) essere evitate?


90

Ho letto su Stack Overflow che alcune funzioni C sono "obsolete" o "dovrebbero essere evitate". Potete per favore fornirmi alcuni esempi di questo tipo di funzione e il motivo?

Quali alternative a queste funzioni esistono?

Possiamo usarli in sicurezza - qualche buona pratica?


3
Credo sia una cosa molto buona da sapere. Alcune funzioni di C dovrebbero davvero essere evitate e utilizzate solo per scopi educativi.
INS

@Felix, sarebbe più facile modificare una singola risposta (contrassegnata come corretta) in futuro. È davvero nell'aria le risposte che probabilmente cambieranno nel tempo. Chissà, forse Jeff fornirà un distintivo di "bidello" alle persone che mantengono aggiornate le risposte nei prossimi anni.
Tim Post

1
@ Tim Post: Ok, cancellerò i miei commenti.
Felix Kling

3
Non lo userei strncpy()come sostituto generale di strcpy(), e non strncat()lo userei mai perché ha l'interfaccia più intuitiva che si possa immaginare - SAPETE cosa specifica il parametro length?
Jonathan Leffler

2
Usare strncpy e strncat è quasi sempre un errore. Certamente non dovrebbero essere usati al posto di strcpy e strcat !! scanf e sprintf sono anche perfettamente utilizzabili se sai come usarli ...
R .. GitHub STOP HELPING ICE

Risposte:


58

Funzioni deprecate non
sicure
Un perfetto esempio di tale funzione è gets () , perché non c'è modo di dirgli quanto è grande il buffer di destinazione. Di conseguenza, qualsiasi programma che legge l'input utilizzando gets () ha una vulnerabilità di overflow del buffer . Per ragioni simili, si dovrebbe usare strncpy () al posto di strcpy () e strncat () al posto di strcat () .

Tuttavia, alcuni altri esempi includono le funzioni tmpfile () e mktemp () a causa di potenziali problemi di sicurezza con la sovrascrittura di file temporanei e che sono sostituite dalla funzione più sicura mkstemp () .

Non rientrante
Altri esempi includono gethostbyaddr () e gethostbyname () che sono non rientranti (e, quindi, non è garantito che siano threadsafe) e sono stati sostituiti da getaddrinfo () e freeaddrinfo () rientrante .

Potresti notare uno schema qui ... o la mancanza di sicurezza (forse non includendo informazioni sufficienti nella firma per implementarla in modo sicuro) o il mancato ingresso sono fonti comuni di deprecazione.

Non aggiornato, non portabile
Alcune altre funzioni diventano semplicemente deprecate perché duplicano funzionalità e non sono portabili come altre varianti. Ad esempio, bzero () è deprecato a favore di memset () .

Thread safety e reentrance
Nel tuo post hai chiesto informazioni sulla thread safety e sul reentrance. C'è una leggera differenza. Una funzione è rientrante se non utilizza uno stato mutabile condiviso. Quindi, ad esempio, se tutte le informazioni di cui ha bisogno vengono passate alla funzione, e tutti i buffer necessari vengono anche passati alla funzione (piuttosto che condivisi da tutte le chiamate alla funzione), allora è rientrante. Ciò significa che thread diversi, utilizzando parametri indipendenti, non rischiano di condividere accidentalmente lo stato. Il rientro è una garanzia più forte della sicurezza dei thread. Una funzione è thread-safe se può essere utilizzata da più thread contemporaneamente. Una funzione è thread-safe se:

  • È rientrante (ovvero non condivide alcuno stato tra le chiamate), oppure:
  • Non è rientrante, ma utilizza la sincronizzazione / blocco come necessario per lo stato condiviso.

In generale, nelle Single UNIX Specification e IEEE 1003.1 (ovvero "POSIX"), non è garantito che qualsiasi funzione di cui non è garantito il rientro sia thread-safe. Quindi, in altre parole, solo le funzioni che sono garantite come rientranti possono essere utilizzate in modo portabile in applicazioni multithread (senza blocco esterno). Ciò non significa, tuttavia, che le implementazioni di questi standard non possano scegliere di rendere sicura per i thread una funzione non rientrante. Ad esempio, Linux spesso aggiunge la sincronizzazione alle funzioni non rientranti per aggiungere una garanzia (oltre a quella della specifica UNIX singola) di sicurezza dei thread.

Stringhe (e Memory Buffer, in generale)
Hai anche chiesto se c'è qualche difetto fondamentale con stringhe / array. Qualcuno potrebbe obiettare che questo è il caso, ma io direi che no, non c'è un difetto fondamentale nella lingua. C e C ++ richiedono di passare separatamente la lunghezza / capacità di un array (non è una proprietà ".length" come in alcuni altri linguaggi). Questo non è un difetto, di per sé. Qualsiasi sviluppatore C e C ++ può scrivere il codice corretto semplicemente passando la lunghezza come parametro dove necessario. Il problema è che diverse API che richiedevano queste informazioni non sono riuscite a specificarle come parametro. Oppure si supponeva che sarebbe stata utilizzata una costante MAX_BUFFER_SIZE. Tali API sono state ora deprecate e sostituite da API alternative che consentono di specificare le dimensioni di array / buffer / stringa.

Scanf (in risposta alla tua ultima domanda)
Personalmente, utilizzo la libreria iostreams C ++ (std :: cin, std :: cout, gli operatori << e >>, std :: getline, std :: istringstream, std :: ostringstream , ecc.), quindi di solito non mi occupo di questo. Se fossi costretto a usare il C puro, però, personalmente userei semplicemente fgetc () o getchar () in combinazione con strtol () , strtoul () , ecc. E analizzerei le cose manualmente, dal momento che non sono un grande fan di vararg o stringhe di formato. Detto questo, per quanto ne so, non ci sono problemi con [f] scanf () , [f] printf (), ecc. fintanto che si creano personalmente le stringhe di formato, non si passano mai stringhe di formato arbitrarie o si consente di utilizzare l'input dell'utente come stringhe di formato e si utilizzano le macro di formattazione definite in <inttypes.h> dove appropriato. (Nota, snprintf () dovrebbe essere usato al posto di sprintf () , ma ciò ha a che fare con la mancata specificazione della dimensione del buffer di destinazione e non con l'uso di stringhe di formato). Dovrei anche sottolineare che, in C ++, boost :: format fornisce una formattazione simile a printf senza vararg.


4
"Deprecato" è una parola forte, che ha un significato specifico quando si discute dello standard C ++. In questo senso, gets (), strcpy () ecc. Non sono deprecati.

4
Finché si distingue tra "deprecato dallo standard C", "deprecato da Michael Aaron Safyan" e "deprecato da persona o persone sconosciute che si spera sappiano di cosa stanno parlando [citazione necessaria]". La domanda è su preferito codifica stile, non sullo standard C, quindi la seconda due sono appropriati. Ma come Neil ho richiesto una doppia presa prima di rendermi conto che le tue affermazioni non intendevano implicare il primo significato.
Steve Jessop

11
strncpydovrebbe essere generalmente evitato. Non fa ciò che la maggior parte dei programmatori presume che faccia. Non garantisce la terminazione (che porta a sovraccarichi del buffer) e riempie le stringhe più brevi (in alcuni casi può peggiorare le prestazioni).
Adrian McCarthy

2
@ Adrian: sono d'accordo con te - né strncpy()né il peggio strncat()è un sostituto ragionevole per le varianti n-less.
Jonathan Leffler

4
Questa risposta diffonde un dogma senza senso su "sostituire strcpy con strncpy - non so perché, ma Microsoft me lo dice". strncpy non è mai stato concepito per essere una versione sicura di strcpy! In faccia è molto più pericoloso. Vedi Perché strlcpy e strlcat sono considerati insicuri? .
Lundin

24

Ancora una volta le persone stanno ripetendo, come un mantra, la ridicola asserzione che la versione "n" delle funzioni str sono versioni sicure.

Se questo era ciò per cui erano destinati, allora avrebbero sempre annullato le stringhe.

Le versioni "n" delle funzioni sono state scritte per l'uso con campi di lunghezza fissa (come le voci di directory nei primi file system) dove il terminatore nul è richiesto solo se la stringa non riempie il campo. Questo è anche il motivo per cui le funzioni hanno strani effetti collaterali che sono inutilmente inefficienti se usati solo come sostituti - prendi strncpy () per esempio:

Se l'array puntato da s2 è una stringa più corta di n byte, i byte nulli vengono aggiunti alla copia dell'array puntato da s1, fino a quando non vengono scritti n byte in tutto.

Poiché i buffer allocati per gestire i nomi dei file sono tipicamente 4kbytes, ciò può portare a un enorme deterioramento delle prestazioni.

Se vuoi versioni "presumibilmente" sicure, procurati - o scrivi le tue - routine strl (strlcpy, strlcat ecc.) Che annullano sempre le stringhe e non hanno effetti collaterali. Si noti, tuttavia, che questi non sono veramente sicuri in quanto possono troncare silenziosamente la stringa: raramente questa è la migliore linea d'azione in qualsiasi programma del mondo reale. Ci sono occasioni in cui questo va bene, ma ci sono anche molte circostanze in cui potrebbe portare a risultati catastrofici (ad esempio la stampa di prescrizioni mediche).


1
Hai ragione strncpy(), ma hai torto strncat(). strncat()non è stato progettato per l'uso con campi a lunghezza fissa, è stato effettivamente progettato per strcat()limitare il numero di caratteri concatenati. È abbastanza facile usarlo come "sicuro strcat()" tenendo traccia dello spazio rimanente nel buffer quando si eseguono più concatenazioni, e ancora più facile usarlo come "sicuro strcpy()" (impostando il primo carattere del buffer di destinazione su '\0'prima chiamandolo). termina strncat() sempre la stringa di destinazione e non scrive messaggi aggiuntivi '\0'.
caf

2
@caf - sì, ma strncat () è totalmente inutile poiché prende come parametro la lunghezza massima da copiare, non la lunghezza del buffer di destinazione. Per prevenire l'overflow del buffer è necessario conoscere la lunghezza della stringa di destinazione corrente, e se lo sai perché dovresti usare strncat () - che deve risolvere di nuovo la lunghezza di destinazione - piuttosto che semplicemente strlcat () la stringa di origine per la fine della stringa di destinazione.
Asta di livello

Ciò ancora non cambia il fatto che tu implichi che strncat()non sempre annulli la destinazione e che sia stato progettato per l'uso con campi a lunghezza fissa, entrambi sbagliati.
caf

2
@chrisharris: strncat()funzionerà correttamente indipendentemente dalla lunghezza della stringa sorgente, mentre strcat()non lo farà. Il problema con strlcat()qui è che non è una funzione C standard.
David Thornley

@caf - hai ragione su strncat (). Non l'ho mai usato perché - come ho sottolineato sopra - è totalmente inutile. Dovrebbe quindi essere ancora evitato.
Asta di livello

19

Diverse risposte qui suggeriscono di utilizzare strncat()over strcat(); Suggerirei che strncat()(e strncpy()) dovrebbe anche essere evitato. Ha problemi che lo rendono difficile da usare correttamente e portano a bug:

  • il parametro di lunghezza a strncat()è relativo (ma non esattamente - vedere il 3 ° punto) al numero massimo di caratteri che possono essere copiati nella destinazione piuttosto che alla dimensione del buffer di destinazione. Ciò rende strncat()più difficile l'uso di quanto dovrebbe essere, in particolare se più elementi verranno concatenati alla destinazione.
  • può essere difficile determinare se il risultato è stato troncato (il che può essere importante o meno)
  • è facile avere un errore off-by-one. Come osserva lo standard C99, "Pertanto, il numero massimo di caratteri che possono finire nell'array puntato da s1è strlen(s1)+n+1" per una chiamata che assomigliastrncat( s1, s2, n)

strncpy()ha anche un problema che può causare bug se cerchi di usarlo in modo intuitivo - non garantisce che la destinazione sia terminata da zero. Per assicurarti di dover gestire in modo specifico quel caso d'angolo, rilasciando '\0'tu stesso un nell'ultima posizione del buffer (almeno in determinate situazioni).

Suggerirei di utilizzare qualcosa come OpenBSD strlcat()e strlcpy()(anche se so che ad alcune persone non piacciono queste funzioni; credo che siano molto più facili da usare in sicurezza di strncat()/ strncpy()).

Ecco un po 'di ciò che Todd Miller e Theo de Raadt hanno detto sui problemi con strncat()e strncpy():

Ci sono diversi problemi riscontrati quando strncpy()e strncat()vengono usati come versioni sicure di strcpy()e strcat(). Entrambe le funzioni trattano la terminazione NUL e il parametro della lunghezza in modi diversi e non intuitivi che confondono anche i programmatori esperti. Inoltre, non forniscono un modo semplice per rilevare quando si verifica il troncamento. ... Di tutti questi problemi, la confusione causata dai parametri di lunghezza e il problema correlato della terminazione NUL sono i più importanti. Quando abbiamo controllato l'albero dei sorgenti di OpenBSD per potenziali falle di sicurezza, abbiamo riscontrato un uso improprio dilagante di strncpy()e strncat(). Sebbene non tutti questi abbiano prodotto falle di sicurezza sfruttabili, hanno chiarito che le regole per l'utilizzo strncpy()e le strncat()operazioni di stringa sicure sono ampiamente fraintese.

L'audit di sicurezza di OpenBSD ha rilevato che i bug con queste funzioni erano "dilaganti". Diversamente gets(), queste funzioni possono essere utilizzate in sicurezza, ma in pratica ci sono molti problemi perché l'interfaccia è confusa, poco intuitiva e difficile da usare correttamente. So che anche Microsoft ha fatto analisi (anche se non so quanti dei loro dati potrebbero aver pubblicato) e di conseguenza ha vietato (o almeno fortemente scoraggiato - il "divieto" potrebbe non essere assoluto) uso di strncat()e strncpy()(tra le altre funzioni).

Alcuni link con maggiori informazioni:


1
È molto meglio tenere traccia della lunghezza delle stringhe al di fuori della stringa stessa. In questo modo, concatenare due stringhe (-with-length) in modo sicuro diventa una questione di un semplice calcolo (per ottenere la dimensione del buffer richiesta), una possibile riallocazione e un file memmove(). (Beh, puoi usare memcpy()nel caso normale quando le corde sono indipendenti.)
Donal Fellows

1
Il tuo secondo punto è completamente sbagliato: termina strncat() sempre la stringa di destinazione.
caf

1
Il tuo secondo punto è sbagliato per strncat(). Tuttavia, è corretto per strncpy(), che ha alcuni altri problemi. strncat()è un sostituto ragionevole per strcat(), ma strncpy()non è un sostituto ragionevole per strcpy().
David Thornley

2
caf e David hanno ragione al 100% sulla mia affermazione che strncat()non sempre termina nulla. Stavo confondendo i comportamenti di strncat()e strncpy()un po '(un altro motivo per cui sono funzioni da evitare - hanno nomi che implicano comportamenti simili, ma in realtà si comportano in modo diverso in modi importanti ...). Ho modificato la mia risposta per correggere questo e aggiungere ulteriori informazioni.
Michael Burr

1
Nota che char str[N] = ""; strncat(str, "long string", sizeof(str));è un buffer overflow se N non è abbastanza grande. La strncat()funzione è troppo facile da usare in modo improprio; non dovrebbe essere usato. Se puoi usare in strncat()sicurezza, avresti potuto usare memmove()o memcpy()invece (e quelli sarebbero più efficienti).
Jonathan Leffler

9

Funzioni di libreria standard che non dovrebbero mai essere utilizzate:

setjmp.h

  • setjmp(). Insieme a longjmp(), queste funzioni sono ampiamente riconosciute come incredibilmente pericolose da usare: portano alla programmazione spaghetti, vengono con numerose forme di comportamento indefinito, possono causare effetti collaterali indesiderati nell'ambiente del programma, come influenzare i valori memorizzati nello stack. Riferimenti: MISRA-C: 2012 regola 21.4, CERT C MSC22-C .
  • longjmp(). Vedi setjmp().

stdio.h

  • gets(). La funzione è stata rimossa dal linguaggio C (come da C11), poiché non era sicura come da progetto. La funzione era già contrassegnata come obsoleta in C99. Usa fgets()invece. Riferimenti: ISO 9899: 2011 K.3.5.4.1, vedere anche nota 404.

stdlib.h

  • atoi()famiglia di funzioni. Questi non hanno gestione degli errori ma invocano un comportamento indefinito ogni volta che si verificano errori. Funzioni del tutto superflue sostituibili con la strtol()famiglia delle funzioni. Riferimenti: MISRA-C: 2012 regola 21.7.

string.h

  • strncat(). Ha un'interfaccia scomoda che viene spesso utilizzata in modo improprio. È principalmente una funzione superflua. Vedi anche le note per strncpy().
  • strncpy(). L'intenzione di questa funzione non è mai stata quella di essere una versione più sicura di strcpy(). Il suo unico scopo è sempre stato quello di gestire un antico formato di stringa su sistemi Unix, e che sia stato incluso nella libreria standard è un errore noto. Questa funzione è pericolosa perché può lasciare la stringa senza terminazione nulla e si sa che i programmatori la usano spesso in modo errato. Riferimenti: perché strlcpy e strlcat sono considerati insicuri? .

Funzioni di libreria standard che dovrebbero essere utilizzate con cautela:

assert.h

  • assert(). Viene fornito con overhead e generalmente non dovrebbe essere utilizzato nel codice di produzione. È preferibile utilizzare un gestore degli errori specifico dell'applicazione che visualizza gli errori ma non chiude necessariamente l'intero programma.

signal.h

stdarg.h

  • va_arg()famiglia di funzioni. La presenza di funzioni di lunghezza variabile in un programma C è quasi sempre un'indicazione di una cattiva progettazione del programma. Dovrebbe essere evitato a meno che tu non abbia requisiti molto specifici.

stdio.h
In generale, l'intera libreria non è raccomandata per il codice di produzione , poiché include numerosi casi di comportamento mal definito e scarsa sicurezza dei tipi.

  • fflush(). Perfettamente adatto da usare per i flussi di output. Richiama un comportamento indefinito se utilizzato per i flussi di input.
  • gets_s(). Versione sicura di gets()inclusa nell'interfaccia di controllo dei limiti C11. È preferibile utilizzare fgets()invece, come da raccomandazione dello standard C. Riferimenti: ISO 9899: 2011 K.3.5.4.1.
  • printf()famiglia di funzioni. Funzioni pesanti di risorse che vengono fornite con molti comportamenti indefiniti e scarsa sicurezza dei tipi. sprintf()ha anche delle vulnerabilità. Queste funzioni dovrebbero essere evitate nel codice di produzione. Riferimenti: MISRA-C: 2012 regola 21.6.
  • scanf()famiglia di funzioni. Vedi osservazioni su printf(). Inoltre, - scanf()è vulnerabile ai sovraccarichi del buffer se non viene utilizzato correttamente. fgets()è preferibile utilizzare quando possibile. Riferimenti: CERT C INT05-C , MISRA-C: 2012 regola 21.6.
  • tmpfile()famiglia di funzioni. Viene fornito con vari problemi di vulnerabilità. Riferimenti: CERT C FIO21-C .

stdlib.h

  • malloc()famiglia di funzioni. Perfettamente adatto per l'uso in sistemi ospitati, anche se sii consapevole di problemi noti in C90 e quindi non trasmettere il risultato . La malloc()famiglia di funzioni non dovrebbe mai essere utilizzata in applicazioni indipendenti. Riferimenti: MISRA-C: 2012 regola 21.3.

    Nota anche che realloc()è pericoloso se sovrascrivi il vecchio puntatore con il risultato di realloc(). Nel caso in cui la funzione fallisca, si crea una perdita.

  • system(). Viene fornito con un sacco di overhead e sebbene sia portatile, spesso è meglio utilizzare invece le funzioni API specifiche del sistema. Viene fornito con vari comportamenti mal definiti. Riferimenti: CERT C ENV33-C .

string.h

  • strcat(). Vedi osservazioni per strcpy().
  • strcpy(). Perfetto da usare, a meno che la dimensione dei dati da copiare non sia sconosciuta o maggiore del buffer di destinazione. Se non viene eseguito alcun controllo della dimensione dei dati in ingresso, potrebbero verificarsi sovraccarichi del buffer. Che non è colpa di per strcpy()sé, ma dell'applicazione chiamante - che strcpy()non è sicuro è principalmente un mito creato da Microsoft .
  • strtok(). Altera la stringa del chiamante e utilizza variabili di stato interne, che potrebbero renderlo pericoloso in un ambiente multi-thread.

Suggerisco di consigliare static_assert()al posto di assert()se la condizione può essere risolta in fase di compilazione. Inoltre, sprintf()può quasi sempre essere sostituito, il snprintf()che è un po 'più sicuro.
user694733

1
Usare strtok()in una funzione A significa che (a) la funzione non può chiamare nessun'altra funzione che usa anche strtok()mentre A la sta usando, e (b) significa che nessuna funzione che chiama A può essere usata strtok()quando chiama A. In altre parole, usando strtok()avvelena la catena della chiamata; non può essere utilizzato in modo sicuro nel codice della libreria perché deve documentare che utilizza strtok()per impedire ad altri utenti di strtok()chiamare il codice della libreria.
Jonathan Leffler

La strncpyè la funzione corretta da usare quando crei una zero imbottita buffer di stringa con dati ricavati da una stringa terminata da zero o un buffer zeri cui dimensione è almeno grande quanto la destinazione. I buffer a riempimento zero non sono molto comuni, ma non sono nemmeno esattamente oscuri.
supercat

7

Alcune persone lo affermerebbero strcpye strcatdovrebbero essere evitate, a favore di strncpye strncat. Questo è un po 'soggettivo, secondo me.

Dovrebbero essere assolutamente evitati quando si ha a che fare con l'input dell'utente, senza dubbio qui.

Nel codice "lontano" dall'utente, quando si sa solo che i buffer sono abbastanza lunghi strcpye strcatpossono essere un po 'più efficienti perché il calcolo del nper passare ai cugini potrebbe essere superfluo.


4
E se disponibile, meglio ancora strlcate strlcpy, poiché la versione 'n' non garantisce la terminazione NULL della stringa di destinazione.
Dan Andreatta

Mi sembra un'ottimizzazione prematura. Ma IIRC strncpy()scriverà scrivere esattamente nbyte, usando caratteri nul se necessario. Come Dan, l'utilizzo di una versione sicura è la scelta migliore IMO.
Bastien Léonard

@ Dan, queste funzioni sono purtroppo non standard e quindi non appartengono a questa discussione
Eli Bendersky

@Eli: ma il fatto che strncat()può essere difficile da usare correttamente e in sicurezza (e dovrebbe essere evitato) è in tema.
Michael Burr

@ Michael: non direi che è difficile da usare correttamente e in sicurezza. Con cura, funziona bene
Eli Bendersky

6

Evitare

  • strtok per i programmi multithread poiché non è thread-safe.
  • gets in quanto potrebbe causare un overflow del buffer

2
Sono casi leggermente diversi. Puoi usare strtok in modo sicuro se sai che il tuo programma non è multithreading o in qualche modo ne blocchi l'accesso, ma non puoi usare gets in modo sicuro, quindi non dovresti mai usarlo.
jcoder

9
Il problema strtok()va un po 'oltre la sicurezza dei thread: non è sicuro nemmeno in un singolo programma a thread a meno che tu non sappia per certo che nessuna funzione che il tuo codice potrebbe chiamare durante l'uso strtok()non lo usa (o rovineranno lo strtok()stato giusto da sotto di te). In effetti, la maggior parte dei compilatori che prendono di mira piattaforme multi-thread si occupano dei strtok()potenziali problemi di thread per quanto riguarda i thread, utilizzando l'archiviazione locale del thread per strtok()i dati statici di. Ma questo ancora non risolve il problema delle altre funzioni che lo usano mentre sei (nello stesso thread).
Michael Burr

Effettivamente e ora starò zitto perché non voglio incoraggiare nessuno a usare strtok perché soffre di molti problemi. Volevo solo sottolineare che è diverso da get, dato che è possibile usarlo in sicurezza mentre è impossibile usarlo in modo sicuro.
jcoder

@ Jimmy: Ci sono spesso estensioni di libreria non standard, oppure potresti scriverne di tue. Il grosso problema strtok()è che mantiene esattamente un buffer su cui lavorare, quindi la maggior parte delle buone sostituzioni richiede di mantenere un valore di buffer intorno e trasferirlo.
David Thornley

strcspnfa la maggior parte di ciò di cui hai bisogno: trova il prossimo separatore di token. Puoi reimplementarne una variante sana strtok.
bluss

5

Probabilmente vale la pena aggiungere di nuovo che strncpy()non è la sostituzione generica di strcpy()ciò che il suo nome potrebbe suggerire. È progettato per campi a lunghezza fissa che non necessitano di un terminatore nul (è stato originariamente progettato per l'uso con voci di directory UNIX, ma può essere utile per cose come i campi della chiave di crittografia).

È facile, tuttavia, da utilizzare strncat()in sostituzione di strcpy():

if (dest_size > 0)
{
    dest[0] = '\0';
    strncat(dest, source, dest_size - 1);
}

(Il iftest può ovviamente essere abbandonato nel caso comune, dove sai che dest_sizeè sicuramente diverso da zero).


5

Controlla anche l'elenco di Microsoft delle API vietate . Si tratta di API (comprese molte già elencate qui) che sono bandite dal codice Microsoft perché spesso vengono utilizzate in modo improprio e portano a problemi di sicurezza.

Potresti non essere d'accordo con tutti loro, ma vale la pena prenderli in considerazione. Aggiungono un'API all'elenco quando il suo uso improprio ha portato a una serie di bug di sicurezza.


2

Quasi tutte le funzioni che si occupano di stringhe terminate con NUL sono potenzialmente pericolose. Se stai ricevendo dati dal mondo esterno e li manipoli tramite le funzioni str * (), allora ti prepari per la catastrofe


2

Non dimenticare sprintf: è la causa di molti problemi. Questo è vero perché l'alternativa, snprintf a volte ha implementazioni diverse che possono rendere il codice non portabile.

  1. linux: http://linux.die.net/man/3/snprintf

  2. windows: http://msdn.microsoft.com/en-us/library/2ts7cx93%28VS.71%29.aspx

Nel caso 1 (linux) il valore di ritorno è la quantità di dati necessari per memorizzare l'intero buffer (se è inferiore alla dimensione del buffer dato, l'output è stato troncato)

Nel caso 2 (windows) il valore restituito è un numero negativo nel caso in cui l'output venga troncato.

In generale dovresti evitare funzioni che non sono:

  1. buffer overflow sicuro (molte funzioni sono già menzionate qui)

  2. thread safe / non rientrante (strtok per esempio)

Nel manuale di ciascuna funzione dovresti cercare parole chiave come: safe, sync, async, thread, buffer, bugs


2
_sprintf()è stato creato da Microsoft prima dell'arrivo dello standard snprintf(), IIRC. ´StringCbPrintf () ´ è abbastanza simile a snprintf()però.
Bastien Léonard

puoi usarlo in sprintfqualche modo in modo sicuro in alcuni casi: sprintf(buffer,"%10s",input);limita i byte nb copiati a 10 (se bufferè char buffer[11]sicuro anche se i dati potrebbero finire troncati.
Jean-François Fabre

2

È molto difficile da usare in scanfsicurezza. Un buon uso di scanfpuò evitare l'overflow del buffer, ma si è comunque vulnerabili a comportamenti indefiniti durante la lettura di numeri che non rientrano nel tipo richiesto. Nella maggior parte dei casi, fgetsseguita da auto-analisi (usando sscanf, strchre così via) è una scelta migliore.

Ma non direi "evitare scanftutto il tempo". scanfha i suoi usi. Ad esempio, supponiamo che tu voglia leggere l'input dell'utente in un chararray lungo 10 byte. Vuoi rimuovere la nuova riga finale, se presente. Se l'utente inserisce più di 9 caratteri prima di una nuova riga, si desidera memorizzare i primi 9 caratteri nel buffer e scartare tutto fino alla nuova riga successiva. Tu puoi fare:

char buf[10];
scanf("%9[^\n]%*[^\n]", buf));
getchar();

Una volta che ti sei abituato a questo idioma, è più breve e in qualche modo più pulito di:

char buf[10];
if (fgets(buf, sizeof buf, stdin) != NULL) {
    char *nl;
    if ((nl = strrchr(buf, '\n')) == NULL) {
        int c;
        while ((c = getchar()) != EOF && c != '\n') {
            ;
        }
    } else {
        *nl = 0;
    }
}

0

In tutti gli scenari di copia / spostamento di stringhe - strcat (), strncat (), strcpy (), strncpy (), ecc. - le cose vanno molto meglio ( più sicure ) se vengono applicate un paio di semplici euristiche:

   1. Sempre NUL-fill i tuoi buffer prima di aggiungere i dati.
   2. Dichiarare buffer di caratteri come [SIZE + 1], con una macro-costante.

Ad esempio, dato:

#define   BUFSIZE   10
char      Buffer[BUFSIZE+1] = { 0x00 };  /* The compiler NUL-fills the rest */

possiamo usare codice come:

memset(Buffer,0x00,sizeof(Buffer));
strncpy(Buffer,BUFSIZE,"12345678901234567890");

relativamente sicuro. Il memset () dovrebbe apparire prima di strncpy (), anche se abbiamo inizializzato Buffer in fase di compilazione, perché non sappiamo quale altro codice immondizia vi sia stato inserito prima che la nostra funzione fosse chiamata. Strncpy () troncherà i dati copiati in "1234567890" e non lo farà NUL. Tuttavia, poiché abbiamo già riempito NUL l'intero buffer - sizeof (Buffer), piuttosto che BUFSIZE - è comunque garantito che ci sia un NUL finale "fuori ambito" che termina comunque, purché vincoliamo le nostre scritture utilizzando BUFSIZE costante, invece di sizeof (Buffer).

Anche Buffer e BUFSIZE funzionano bene per snprintf ():

memset(Buffer,0x00,sizeof(Buffer));
if(snprintf(Buffer,BUFIZE,"Data: %s","Too much data") > BUFSIZE) {
    /* Do some error-handling */
}   /* If using MFC, you need if(... < 0), instead */

Anche se snprintf () scrive specificamente solo caratteri BUFIZE-1, seguiti da NUL, funziona in modo sicuro. Quindi "sprechiamo" un byte NUL estraneo alla fine del Buffer ... evitiamo condizioni di overflow del buffer e stringa non terminata, per un costo di memoria piuttosto basso.

La mia chiamata su strcat () e strncat () è più rigida: non usarli. È difficile usare strcat () in modo sicuro e l'API per strncat () è così controintuitiva che lo sforzo necessario per usarlo correttamente nega qualsiasi vantaggio. Propongo il seguente drop-in:

#define strncat(target,source,bufsize) snprintf(target,source,"%s%s",target,source)

Si è tentati di creare un drop-in strcat (), ma non è una buona idea:

#define strcat(target,source) snprintf(target,sizeof(target),"%s%s",target,source)

perché target può essere un puntatore (quindi sizeof () non restituisce le informazioni di cui abbiamo bisogno). Non ho una buona soluzione "universale" per le istanze di strcat () nel codice.

Un problema che incontro frequentemente con i programmatori "strFunc () - aware" è un tentativo di proteggersi dai buffer overflow utilizzando strlen (). Questo va bene se è garantito che i contenuti saranno terminati con NUL. Altrimenti, strlen () stesso può causare un errore di sovraccarico del buffer (che di solito porta a una violazione della segmentazione o ad altre situazioni di core dump), prima che tu raggiunga il codice "problematico" che stai cercando di proteggere.


-2

atoi non è thread-safe. Uso invece strtol, per raccomandazione dalla pagina man.


5
Sembra che si applichi a una particolare implementazione. Non c'è motivo per cui strtol()sarebbe thread-safe e atoi()non lo sarebbe.
David Thornley

2
La raccomandazione di utilizzare strtol non ha nulla a che fare con la sicurezza dei thread, ma con la gestione degli errori. Non sono nemmeno sicuro da quale pagina man hai ottenuto queste informazioni - Non riesco a trovare alcuna raccomandazione qui sotto man atoi(dovrebbe esserci, però).
Lundin
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.