2009-11-21 5 views
14

gcc 4.4.1snprintf Verwendung zu Pufferüberläufen

C99

ich verwende snprintf wie dies zu vermeiden einen Pufferüberlauf zu vermeiden:

char err_msg[32] = {0}; 
snprintf(err_msg, sizeof(err_msg) - 1, "[ ST_ENGINE_FAILED ]"); 

ich die -1 hinzugefügt, um den Raum zu reservieren Null-Terminator für den Fall, dass die Zeichenfolge mehr als 32 Byte lang ist.

Bin ich richtig in meinem Denken?

+2

Randbemerkung: GCC nicht C99 unterstützt: http: // gcc. gnu.org/c99status.html –

+2

Ist Ihnen jedoch eine moderne Umgebung bekannt, in der die gcc - und Standardbibliotheks - Kombination "snprintf" nicht enthält? – LnxPrgr3

+3

Als ich vor ein oder zwei Jahren MinGW verwendet habe, hat es tatsächlich Microsofts '_snprintf()' aufgerufen, das sich nicht wie der Standard 'snprintf()' verhält (ich glaube, es wird die Zeichenkette nicht immer beenden). –

Antwort

23

Wie andere gesagt haben, brauchen Sie in diesem Fall nicht die -1. Wenn das Array eine feste Größe hat, würde ich stattdessen strncpy verwenden. Es wurde für das Kopieren von Zeichenfolgen erstellt - sprintf wurde für schwierige Formatierung erstellt. Wenn jedoch die Größe des Arrays unbekannt ist oder Sie versuchen zu ermitteln, wie viel Speicher für eine formatierte Zeichenfolge erforderlich ist. Dies ist, was ich wirklich wie über die Standard-Version von snprintf angegeben:

char* get_error_message(char const *msg) { 
    size_t needed = snprintf(NULL, 0, "%s: %s (%d)", msg, strerror(errno), errno); 
    char *buffer = malloc(needed+1); 
    sprintf(buffer, "%s: %s (%d)", msg, strerror(errno), errno); 
    return buffer; 
} 

Kombinieren Sie diese Funktion mit va_copy und Sie können sehr sicher formatiert String-Operationen erstellen. 

+3

Verwenden Sie nicht strncpy(), wenn die Zeichenfolge möglicherweise zu groß ist, um in das Ziel zu passen. strncpy() macht ** NOT ** null-terminiert, was es kopiert, wenn es zu lang ist. Außerdem kopiert es immer N Zeichen - selbst wenn die Quelle 1 Byte und das Ziel 1 Megabyte ist. –

+1

@ Jonathan: Nein. während Sie Recht haben, dass strncpy nicht mit \ 0 endet, stoppt * *, wenn es einen String-Terminator in der Quellzeichenfolge findet. – Nicholaz

+1

Für Puffer mit fester Länge verwende ich normalerweise 'strncpy (dest, src, sizeof (dest)); dest [sizeof (dest) -1] = '\ 0'; 'Das garantiert NULL-Beendigung und ist weniger mühsam als' snprintf', um nicht zu erwähnen, dass viele Leute 'snprintf (dest, sizeof (dest), src) benutzen ; und sind sehr überrascht, wenn ihre Programme willkürlich abstürzen. –

-1

sizeof gibt die Anzahl der Bytes zurück, die der Datentyp im Speicher verwendet, und nicht die Länge der Zeichenfolge. Z.B. sizeof (int) gibt '4' Bytes auf einem 32-Bit-System zurück (gut, abhängig von der Implementierung, denke ich). Da Sie in Ihrem Array eine Konstante verwenden, können Sie diese problemlos an printf übergeben.

+0

Er wendet 'sizeof' auf das Ziel-Array an, was völlig korrekt ist. – caf

+0

nein, 'sizeof (err_msg)' ergibt 32. –

+0

32 ist korrekt. In diesem Fall will er nicht die Größe der Zeichenfolge (die von strlen angegeben wird), er will die Pufferkapazität "err_msg" (um zu garantieren, dass sie nicht über ihr Ende hinaus schreiben wird). –

10

Sie brauchen nicht die -1, wie die Referenzzustände:

Die Funktionen snprintf() und vsnprintf() (einschließlich der Hinter ‚nicht mehr als Größe Bytes schreiben \ 0 ').

Beachten Sie die "einschließlich des nachgestellten '\ 0'" Teil

2

Nach snprintf(3):

Die Funktionen snprintf() und vsnprintf() nicht mehr als size Bytes schreiben (einschließlich der Hinter '\0') .

7

Keine Notwendigkeit für -1. C99 snprintfimmer nullterminiert. Size-Argument gibt die Größe des Ausgabepuffers einschließlich Zero Terminator an. Der Code, so wird

char err_msg[32]; 
int ret = snprintf(err_msg, sizeof err_msg, "[ ST_ENGINE_FAILED ]"); 

ret gedruckt tatsächliche Anzahl von Zeichen enthält (ohne Null Terminator).

jedoch nicht zu verwechseln mit Microsofts _snprintf (pre-C99), das tut nicht Null beenden, und, was das betrifft, hat völlig anderes Verhalten (zB Rückkehr -1 statt Möchtegern-Länge im Fall gedruckt wenn der Puffer nicht groß genug ist). Wenn Sie _snprintf verwenden, sollten Sie den gleichen Code wie in Ihrer Frage verwenden.

1

Für das Beispiel gegeben, sollten Sie diese stattdessen tun:

char err_msg[32]; 
strncpy(err_msg, "[ ST_ENGINE_FAILED ]", sizeof(err_msg)); 
err_msg[sizeof(err_msg) - 1] = '\0'; 

oder noch besser:

char err_msg[32] = "[ ST_ENGINE_FAILED ]"; 
+1

Wie in einem Kommentar zu einer anderen Antwort erwähnt: Verwenden Sie nicht strncpy(), wenn die Zeichenfolge zu groß für das Ziel ist trncpy() terminiert NULL NICHT, was es kopiert, wenn es zu lang ist. Außerdem kopiert es immer N Zeichen - selbst wenn die Quelle 1 Byte und das Ziel 1 Megabyte ist. –

+1

@ Jonathan Leffler, Ihre Beschreibung, wie viele Bytes 'strncpy()' ist falsch. "Es werden höchstens n Bytes von src kopiert." Ich habe das Beispiel angepasst, um die Null-Terminierung zu beheben. –

+0

@anacrolix: Ihr Beispiel garantiert keine NULL-Terminierung. Es garantiert, dass 'strncpy()' niemals das letzte Zeichen im Puffer überschreibt. Sie müssen explizit 'err_msg [sizeof (err_msg) -1] =' \ 0 '; 'eingeben, um die Terminierung zu erhalten. –

Verwandte Themen