2009-08-16 14 views
19

vi uso de este patrón para concatenar en una cadena en algún código que estaba trabajando:¿Es seguro sprintf (buffer, "% s [...]", buffer, [...])?

sprintf(buffer, "%s <input type='file' name='%s' />\r\n", buffer, id); 
sprintf(buffer, "%s</td>", buffer); 

y estoy bastante seguro de que no es segura C. Se dará cuenta de que buffer es a la vez la salida y la primera entrada.

Además de la posibilidad obvia de desbordamiento de memoria intermedia, creo que no hay garantía de que la memoria intermedia no cambie entre el inicio y el final de la función (es decir, no hay garantía de qué de buffer será durante la ejecución de la función). La firma de sprintf especifica, además, que la cadena de destino es restrict ed.

También recuerdo un informe de speculative writing in memcpy, y no veo ninguna razón por la que alguna biblioteca C pueda hacer lo mismo en un sprintf. En este caso, por supuesto, estaría escribiendo en su fuente. ¿Entonces es este comportamiento seguro?

FYI, he propuesto:

char *bufEnd = buffer + strlen(buffer); 
/* sprintf returns the number of f'd and print'd into the s */ 
bufEnd += sprintf(bufEnd, " <input type='file' name='%s' />\r\n", id); 

para reemplazarlo.

+0

Incluso si es * seguro * (no se bloqueará, etc.) puedo imaginarlo generando un resultado diferente al esperado. –

+0

@AndrewMedico ¿Cómo es eso? – cat

Respuesta

18

Desde el glibc sprintf() documentation:

El comportamiento de esta función es indefinido si la copia se lleva a cabo entre los objetos que se superponen, para ejemplo, si s también se da como argumento a imprimir bajo control de la conversión '% s'.

Puede ser seguro en una implementación particular; pero no puedes contar con que sea portátil.

No estoy seguro de que su propuesta sea segura en todos los casos tampoco. Todavía podría superponer búferes. Es tarde y mi esposa me está repitiendo, pero creo que todavía podría tener el caso en el que desee volver a utilizar la cadena original en la cadena concatenada y sobrescriba el carácter nulo, por lo que la implementación de sprintf podría no saber dónde se reutilizó cadena termina

Es posible que desee quedarse con un snprint() en un buffer temporal, luego strncat() en el buffer original.

+1

De acuerdo, solo quería un control de cordura. [POSIX dice lo mismo] (http://www.opengroup.org/onlinepubs/9699919799/functions/sprintf.html): > Si el copiado tiene lugar entre objetos que se superponen como resultado de una llamada a sprintf() o snprintf(), los resultados no están definidos. –

+0

En realidad, no estoy superponiendo almacenamientos intermedios en el segundo: es un buffer estrictamente diferente. No uso el original. –

+0

A menos que esté viendo algo que yo no veo, lo cual es completamente posible. –

4

En este caso específico, se va a trabajar porque la cadena en buffer será el primero que se va a introducir en buffer (de nuevo, inútil), por lo que debe utilizar strcat() en lugar de obtener el [casi] misma efecto.

Pero, si usted está tratando de combinar strcat() con las posibilidades de formateo de sprintf(), es posible que intente esto:

sprintf(&buffer[strlen(buffer)], " <input type='file' name='%s' />\r\n", id);
3

Si desea concatenar texto con formato a la final de un búfer usando printf(), Te recomiendo que uses un número entero para hacer un seguimiento de la posición final.

int i = strlen(buffer); 
i += sprintf(&buffer[i], " <input type='file' name='%s' />\r\n", id); 
i += sprintf(&buffer[i], "</td>"); 

o:

int i = strlen(buffer); 
i += sprintf(&buffer[i], " <input type='file' name='%s' />\r\n", id); 
strcat(&buffer[i], "</td>"); 

Y antes de que las personas se vuelven locos downvoting esto ("Esto no es seguro Puede desbordar el búfer!"), Sólo estoy frente a una forma razonable para construir una cadena formateada en C/C++.

+0

Creo que su sugerencia es funcionalmente la misma que mi reemplazo propuesto, pero utilizando una notación ligeramente diferente. Aunque puedo ver por qué algunos prefieren verlo escrito de esta manera. –