c++ - solucion - desbordamiento de pila
Consecuencias de este desbordamiento de bĂșfer? (11)
Como lo han dicho otros, usted tiene toda la razón al suponer que esto no es bueno, y la razón por la que no ve esto es el relleno. Intenta valgrind
esto con esto, esto debería encontrar ese error definitivamente.
Así que aquí creo que tengo un pequeño problema de desbordamiento de búfer que encontré al revisar el código de otra persona. Inmediatamente me pareció incorrecto y potencialmente peligroso, pero debo admitir que no pude explicar las consecuencias REALES de este "error", si lo hubiera.
Había escrito una aplicación de prueba para demostrar el error, pero descubrí (para mi consternación) que parece funcionar correctamente independientemente del desbordamiento. Quiero creer que esto es solo por casualidad, pero quería algunos comentarios para determinar si mi pensamiento estaba mal, o si realmente hay un problema aquí que simplemente no muestra su cabeza en mi aplicación de prueba.
El código del problema (creo que es, de todos modos):
char* buffer = new char[strlen("This string is 27 char long" + 1)];
sprintf(buffer, "This string is 27 char long");
Ahora, la razón por la que esto se destacó y quiero strlen
como un posible desbordamiento de búfer es debido al primer strlen
. Debido a la aritmética de punteros, la ubicación "incorrecta" del + 1
hará que el strlen
devuelva 26
lugar de 27
(tomando la longitud de "su cadena es de 27 caracteres"). Creo que sprintf
imprime 27 caracteres en el búfer y ha provocado un desbordamiento del búfer.
¿Es esa una evaluación correcta?
Escribí una aplicación de prueba para demostrar esto a la persona que estaba viendo el código y descubrí que incluso en el depurador la cadena se imprimirá correctamente. También intenté poner otras variables en la pila y el montón antes y después de este código para ver si podía afectar las áreas vecinas de la memoria, pero todavía estaba recibiendo la salida correcta. Me doy cuenta de que mi memoria de pila recién asignada podría no ser adyacente, lo que explicaría la falta de un desbordamiento útil, pero realmente quería confirmar con las opiniones de los demás si de hecho esto es un problema.
Dado que esta es una "pregunta" bastante simple, sería bueno si pudiera apoyar su respuesta con algún tipo de referencia también. Si bien valoro y agradezco sus comentarios, no voy a aceptar "sí, lo es" como respuesta final. Gracias amablemente por adelantado.
Actualización: Muchas buenas respuestas con mucha información adicional. Desafortunadamente, no puedo aceptarlas todas. Gracias por compartir sus conocimientos y por ser mi "segunda opinión". Aprecio la ayuda.
Declaración correcta. Como está pasando la dirección del segundo carácter de la cadena a strlen (), como resultado, obtiene la longitud un carácter menos. Aparte de eso, el problema principal es con sprintf (), esa es una de las razones por las que no es seguro.
Incluso esto compila y ejecuta (también puede fallar).
char* x = new char;
sprintf(x, "This is way longer than one character");
printf("%s", x);
Para evitar este problema peligroso, debe usar versiones seguras de esta función como snprintf () o asprintf () en GCC o sprintf_s () en MSVC.
Como referencias, consulte la documentación de la biblioteca GNU C a este respecto y también la nota de seguridad del artículo sprintf () de MSDN.
El problema es que estás escribiendo en algún lugar de la memoria, pero no en la pila. Por lo tanto, es difícil ver realmente lo que está mal. Si desea ver los daños, intente asignar la cadena en la pila
char buffer[strlen("This string is 27 char long" + 1)];
y lo escriben más allá de eso. Se escribirán otras variables, también puede agregar algún código para ejecutarse si realmente sabe cómo funciona el binario.
Para explotar un desbordamiento de búfer como ese, necesita escribir los datos que desea, luego encontrar una manera de "saltar" a estos datos para ser ejecutados.
Es correcto que la aritmética de punteros en este ejemplo produciría una longitud incorrecta (más corta) pasada a nueva. La razón más probable por la que no puede realizar este bloqueo es porque existe cierta incertidumbre en cuanto a cuánto espacio de búfer realmente proporciona la asignación de memoria.
La biblioteca puede proporcionar un búfer más grande que el solicitado. Además, también es posible que lo que sigue a su búfer esté precedido por un encabezado de asignación sujeto a las reglas de alineación de palabras de máquina. Esto significa que podría haber hasta tres bytes de relleno (dependiendo de la plataforma) antes del encabezado de asignación muy próximo.
Incluso si sobrescribió el siguiente encabezado de asignación (que se usa para administrar los bloques de memoria asignados), no se manifestará como un problema hasta que el propietario del siguiente bloque intente devolverlo al montón.
La razón por la que la cadena se está imprimiendo bien en el depurador es que, como parte del sprintf, el carácter NULL final se escribe en la memoria (en este caso, más allá del búfer asignado) y cuando se trata de leer la cadena, el carácter NULO está presente para terminar la cadena como se esperaba.
El problema es que el byte que contiene el carácter NULO no se ha asignado como parte del new
original y, por lo tanto, podría usarse para una asignación diferente más adelante. En este caso, cuando vuelva a leer la cadena después, es probable que obtenga su cadena original con la basura agregada.
Lo intenté con asignaciones de pila, las variables no son continuas en la memoria en este caso. Es por eso que es difícil hacer un desbordamiento de búfer en este caso.
Comprar probarlo con desbordamiento de pila
#include "stdio.h"
#include "string.h"
int main()
{
unsigned int y = (0xFFFFFFFF);
char buffer[strlen("This string is 27 char long" + 1)];
unsigned int x = (0xFFFFFFFF);
sprintf(buffer, "This string is 27 char long");
printf("X (%#x) is %#x, Y (%#x) is %#x, buffer ''%s'' (%#x) /n", &x, x,&y, y, buffer, buffer);
return 0;
}
Verás que Y está corrompida.
Muchas implementaciones históricas de malloc
colocan los datos de la contabilidad inmediatamente antes y / o después del bloque asignado. Es posible que esté sobrescribiendo dichos datos, en cuyo caso no vería ningún error / bloqueo hasta que intente liberar la memoria (o quizás sea libre, sea cual sea el siguiente bloque). Del mismo modo, es posible que la información de la contabilidad para una asignación posterior sobrescriba su cadena.
Sospecho que las implementaciones modernas de malloc
hacen un esfuerzo para protegerse contra la corrupción del montón al rellenar las asignaciones con datos de verificación de integridad, por lo que si tiene suerte, no ocurrirá nada malo o puede recibir un mensaje de advertencia durante una posterior asignación / operación gratuita.
Sí, estás en lo correcto. El búfer asignado será 2 bytes demasiado pequeño para contener la cadena.
Dado que esto se asigna en el montón, sería posible que esto resulte en una corrupción del montón. Sin embargo, la probabilidad de que eso ocurra depende de lo que otras asignaciones y liberaciones de memoria hayan ocurrido antes de este punto y también del uso del administrador de pilas. Ver desbordamiento de montón para más.
Su valoración es correcta. [editar] con la adición de la corrección mencionada por James Curran. [/ editar]
Probablemente, su aplicación de prueba no mostró el problema porque la asignación se redondea al siguiente múltiplo de 4, 8 o 16 (que son granularidades de asignación comunes).
Esto significa que debería poder demostrar con una cadena de 31 caracteres.
Alternativamente, use un generador de perfiles de memoria nativo de "instrumentación" que pueda colocar los bytes de guarda cerca de dicha asignación.
Tu evaluación es correcta, excepto que el springf colocará 28 caracteres en el búfer contando el NUL de final de cadena al final (es por eso que necesitabas el "+1" mal colocado en primer lugar)
Tenga en cuenta que, en mi experiencia, si algo falla fuera de un depurador, pero funciona al avanzar en el depurador, en el 100% del tiempo, ha superado un búfer local. Los depuradores empujan mucho más en la pila, por lo que es menos probable que se haya sobrescrito algo importante.
Tu verdadero problema es que estas escribiendo
char* buffer = new char[strlen("This string is 27 char long" + 1)];
en lugar de
char* buffer = new char[strlen("This string is 27 char long") + 1];
Lo que significa que en la primera le estás dando a strlen () una dirección que no es el principio de tu cadena .
Prueba este código:
const char szText[] = "This string is 27 char long";
char* buffer = new char[strlen(szText) + 1];
sprintf(buffer, szText);