En todos los escenarios de copia / movimiento de cadenas, strcat (), strncat (), strcpy (), strncpy (), etc., las cosas van mucho mejor ( más seguras ) si se aplican un par de heurísticas simples:
1. Siempre relleno NUL su (s) búfer (s) antes de agregar datos.
2. Declare los búferes de caracteres como [SIZE + 1], con una macro-constante.
Por ejemplo, dado:
#define BUFSIZE 10
char Buffer[BUFSIZE+1] = { 0x00 }; /* The compiler NUL-fills the rest */
podemos usar código como:
memset(Buffer,0x00,sizeof(Buffer));
strncpy(Buffer,BUFSIZE,"12345678901234567890");
relativamente seguro. Memset () debería aparecer antes de strncpy (), aunque inicializamos Buffer en tiempo de compilación, porque no sabemos qué basura colocó otro código en él antes de que se llamara a nuestra función. Strncpy () truncará los datos copiados a "1234567890" y no los terminará en NUL. Sin embargo, dado que ya hemos llenado con NUL todo el búfer, tamaño de (búfer), en lugar de BUFSIZE, se garantiza que habrá un NUL final "fuera de alcance" final de todos modos, siempre que restrinjamos nuestras escrituras usando BUFSIZE constante, en lugar de sizeof (Buffer).
Buffer y BUFSIZE también funcionan bien para 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 */
Aunque snprintf () escribe específicamente solo caracteres BUFIZE-1, seguidos de NUL, esto funciona de forma segura. Así que "desperdiciamos" un byte NUL extraño al final del búfer ... evitamos tanto el desbordamiento del búfer como las condiciones de cadena sin terminar, por un costo de memoria bastante pequeño.
Mi llamada a strcat () y strncat () es más estricta: no los use. Es difícil usar strcat () de forma segura, y la API para strncat () es tan contraintuitiva que el esfuerzo necesario para usarla correctamente niega cualquier beneficio. Propongo el siguiente drop-in:
#define strncat(target,source,bufsize) snprintf(target,source,"%s%s",target,source)
Es tentador crear un strcat () drop-in, pero no es una buena idea:
#define strcat(target,source) snprintf(target,sizeof(target),"%s%s",target,source)
porque el objetivo puede ser un puntero (por lo tanto, sizeof () no devuelve la información que necesitamos). No tengo una buena solución "universal" para las instancias de strcat () en su código.
Un problema que encuentro con frecuencia de los programadores "strFunc () - consciente" es un intento de proteger contra desbordamientos de búfer mediante strlen (). Esto está bien si se garantiza que el contenido tiene terminación NUL. De lo contrario, strlen () en sí mismo puede causar un error de saturación del búfer (que generalmente conduce a una violación de segmentación u otra situación de volcado de núcleo), antes de que llegue al código "problemático" que está tratando de proteger.