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.