In allen Szenarien zum Kopieren / Verschieben von Zeichenfolgen - strcat (), strncat (), strcpy (), strncpy () usw. - läuft es viel besser ( sicherer ), wenn ein paar einfache Heuristiken erzwungen werden:
1. Immer NUL-füllen Ihre Puffer vor dem Hinzufügen von Daten.
2. Deklarieren Sie Zeichenpuffer als [GRÖSSE + 1] mit einer Makrokonstante.
Zum Beispiel gegeben:
#define BUFSIZE 10
char Buffer[BUFSIZE+1] = { 0x00 }; /* The compiler NUL-fills the rest */
Wir können Code verwenden wie:
memset(Buffer,0x00,sizeof(Buffer));
strncpy(Buffer,BUFSIZE,"12345678901234567890");
relativ sicher. Das memset () sollte vor dem strncpy () erscheinen, obwohl wir Buffer zur Kompilierungszeit initialisiert haben, da wir nicht wissen, welchen Müll anderer Code darin abgelegt hat, bevor unsere Funktion aufgerufen wurde. Strncpy () schneidet die kopierten Daten auf "1234567890" ab und beendet sie nicht mit NUL. Da wir jedoch bereits den gesamten Puffer mit NUL gefüllt haben - sizeof (Buffer) anstelle von BUFSIZE -, wird garantiert, dass NUL endgültig "außerhalb des Gültigkeitsbereichs" endet, solange wir unsere Schreibvorgänge mit BUFSIZE einschränken konstant anstelle von sizeof (Buffer).
Buffer und BUFSIZE funktionieren ebenfalls gut für 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 */
Obwohl snprintf () speziell nur BUFIZE-1-Zeichen gefolgt von NUL schreibt, funktioniert dies sicher. Wir "verschwenden" also ein fremdes NUL-Byte am Ende von Buffer ... wir verhindern sowohl Pufferüberlauf als auch nicht abgeschlossene String-Bedingungen für einen relativ geringen Speicheraufwand.
Mein Aufruf von strcat () und strncat () ist schwieriger: Verwenden Sie sie nicht. Es ist schwierig, strcat () sicher zu verwenden, und die API für strncat () ist so kontraintuitiv, dass der Aufwand für die ordnungsgemäße Verwendung jeden Vorteil zunichte macht. Ich schlage folgendes Drop-In vor:
#define strncat(target,source,bufsize) snprintf(target,source,"%s%s",target,source)
Es ist verlockend, ein Drop-In für strcat () zu erstellen, aber keine gute Idee:
#define strcat(target,source) snprintf(target,sizeof(target),"%s%s",target,source)
da das Ziel möglicherweise ein Zeiger ist (daher gibt sizeof () nicht die benötigten Informationen zurück). Ich habe keine gute "universelle" Lösung für Instanzen von strcat () in Ihrem Code.
Ein Problem, auf das ich häufig von "strFunc () - fähigen" Programmierern stoße, ist der Versuch, mit strlen () vor Pufferüberläufen zu schützen. Dies ist in Ordnung, wenn garantiert ist, dass der Inhalt NUL-terminiert ist. Andernfalls kann strlen () selbst einen Pufferüberlauffehler verursachen (der normalerweise zu einer Segmentierungsverletzung oder einer anderen Core-Dump-Situation führt), bevor Sie jemals den "problematischen" Code erreichen, den Sie schützen möchten.