2009-11-21 13 views
14

gcc 4.4.1Usando snprintf para evitar desbordamientos de búfer

C99

estoy usando snprintf como esto para evitar una saturación del búfer:

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

que añade el -1 para reservar espacio para el terminador nulo en caso de que la cadena tenga más de 32 bytes de longitud.

¿Estoy en lo correcto en mi forma de pensar?

+2

Nota al margen: GCC no soporta C99: http: // gcc. gnu.org/c99status.html –

+2

¿Conoces, sin embargo, un entorno moderno donde el combo de biblioteca estándar y gcc no incluye 'snprintf'? – LnxPrgr3

+3

Cuando usaba MinGW hace uno o dos años, en realidad llamaba a '_snprintf()' de Microsoft, que no se comporta como el 'snprintf()' estándar (creo que no siempre termina la cadena). –

Respuesta

23

Como han dicho otros, no necesita el -1 en este caso. Si la matriz es de tamaño fijo, usaría strncpy en su lugar. Fue hecho para copiar cadenas. El sprintf fue hecho para hacer un formateo difícil. Sin embargo, si el tamaño de la matriz es desconocido o si está tratando de determinar cuánto espacio de almacenamiento es necesario para una cadena formateada. Esto es lo que realmente me gusta de la norma especificada versión de snprintf:

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; 
} 

combinar esta función con va_copy y se pueden crear operaciones de cadena con formato muy seguro. 

+3

No use strncpy() si la cadena puede ser demasiado grande para caber en el objetivo; strncpy() ** NO ** termina nulo lo que copia si es demasiado largo. Además, siempre copia N caracteres, incluso si la fuente es de 1 byte y el objetivo es de 1 megabyte. –

+1

@Jonathan: No. Mientras tiene razón en que strncpy no termina con \ 0, * does * se detiene si encuentra un terminador de cadena en la cadena fuente. – Nicholaz

+1

Para almacenamientos intermedios de longitud fija, generalmente uso 'strncpy (dest, src, sizeof (dest)); dest [sizeof (dest) -1] = '\ 0'; 'Eso garantiza la terminación NULL y es menos complicado que' snprintf' sin mencionar que muchas personas usan 'snprintf (dest, sizeof (dest), src) ; 'en cambio, están muy sorprendidos cuando sus programas fallan arbitrariamente. –

-1

sizeof devolverá el número de bytes que el tipo de datos usará en la memoria, no la longitud de la cadena. P.ej. sizeof (int) devuelve '4' bytes en un sistema de 32 bits (bueno, dependiendo de la implementación, supongo). Como usas una constante en tu matriz, puedes pasarla a la impresora.

+0

Está aplicando 'sizeof' a la matriz de destino, que es completamente correcta. – caf

+0

no, 'sizeof (err_msg)' dará 32. –

+0

32 es correcto. En ese caso, él no quiere el tamaño de la cadena (que está dada por strlen), quiere la capacidad del buffer 'err_msg' (para garantizar que no va a escribir más allá de su final). –

10

No es necesario el -1, como los estados de referencia:

Las funciones snprintf() y vsnprintf() no escriba más de size bytes (incluyendo el arrastre '\ 0 ').

Nota del "incluyendo el terminador '\ 0'" parte

2

Según snprintf(3):

Las funciones snprintf() y vsnprintf() No escriba más de size bytes (incluyendo el arrastre '\0') .

7

No hay necesidad de -1. C99 snprintfsiempre termina en cero. El argumento de tamaño especifica el tamaño del buffer de salida que incluye cero terminador. El código, por lo tanto, se convierte en

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

ret contiene el número real de caracteres impresos (excluyendo cero terminador).

Sin embargo, no confunda con Microsoft _snprintf (pre-C99), que hace no nulo por terminado, y, para el caso, tiene un comportamiento completamente diferente (por ejemplo, volviendo -1 en lugar de los aspirantes a ser impreso longitud en el caso si el buffer no es lo suficientemente grande). Si usa _snprintf, debe usar el mismo código que en su pregunta.

1

Para el ejemplo dado, que debería estar haciendo esto en su lugar:

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

o incluso mejor:

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

Como se señala en un comentario a otra respuesta: No use strncpy() si la cadena puede ser demasiado grande para caber en el objetivo; trncpy() NO termina nulo lo que copia si es demasiado largo. Además, siempre copia N caracteres, incluso si la fuente es de 1 byte y el objetivo es de 1 megabyte. –

+1

@Jonathan Leffler, su descripción de cuántos bytes 'strncpy()' es incorrecto. "Se copian como máximo n bytes de src". Ajusté el ejemplo para corregir la terminación nula. –

+0

@anacrolix: su ejemplo no garantiza la terminación NULL. Garantiza que 'strncpy()' nunca sobrescribirá el último carácter en el búfer. Tienes que hacer explícitamente 'err_msg [sizeof (err_msg) -1] = '\ 0';' para obtener la terminación. –

Cuestiones relacionadas