Estoy ejecutando mi archivo a.out. Después de la ejecución, el programa se ejecuta durante un tiempo y luego sale con el mensaje:
**** stack smashing detected ***: ./a.out terminated*
*======= Backtrace: =========*
*/lib/tls/i686/cmov/libc.so.6(__fortify_fail+0x48)Aborted*
¿Cuáles podrían ser las posibles razones para esto y cómo lo rectifico?
Respuestas:
Stack Smashing aquí en realidad se debe a un mecanismo de protección utilizado por gcc para detectar errores de desbordamiento del búfer. Por ejemplo en el siguiente fragmento:
El compilador (en este caso gcc) agrega variables de protección (llamadas canarias) que tienen valores conocidos. Una cadena de entrada de tamaño superior a 10 provoca la corrupción de esta variable, lo que hace que SIGABRT finalice el programa.
Para obtener una idea, puede intentar deshabilitar esta protección de gcc usando la opción
-fno-stack-protector
mientras compila. En ese caso, obtendrá un error diferente, muy probablemente un error de segmentación al intentar acceder a una ubicación de memoria ilegal. Tenga en cuenta que-fstack-protector
siempre debe estar activado para las versiones de lanzamiento, ya que es una característica de seguridad.Puede obtener información sobre el punto de desbordamiento ejecutando el programa con un depurador. Valgrind no funciona bien con los errores relacionados con la pila, pero como un depurador, puede ayudarlo a determinar la ubicación y el motivo del bloqueo.
fuente
Ejemplo de reproducción mínima con análisis de desmontaje
C Principal
GitHub aguas arriba .
Compilar y ejecutar:
falla como se desea:
Probado en Ubuntu 16.04, GCC 6.4.0.
Desmontaje
Ahora nos fijamos en el desmontaje:
que contiene:
Observe los prácticos comentarios agregados automáticamente por
objdump
's módulo de inteligencia artificial .Si ejecuta este programa varias veces a través de GDB, verá que:
myfunc
es exactamente lo que modifica la dirección del canarioEl canario se aleatorizó configurándolo con
%fs:0x28
, que contiene un valor aleatorio como se explica en:Intentos de depuración
De ahora en adelante, modificamos el código:
ser en su lugar:
para ser más interesante
Luego intentaremos ver si podemos identificar la
+ 1
llamada del culpable con un método más automatizado que simplemente leer y comprender todo el código fuente.gcc -fsanitize=address
para habilitar el desinfectante de direcciones de Google (ASan)Si vuelve a compilar con este indicador y ejecuta el programa, genera:
seguido por algo más de salida de color.
Esto señala claramente la problemática línea 12.
El código fuente para esto está en: https://github.com/google/sanitizers pero, como vimos en el ejemplo, ya está integrado en GCC.
ASan también puede detectar otros problemas de memoria, como pérdidas de memoria: ¿Cómo encontrar la pérdida de memoria en un código / proyecto C ++?
Valgrind SGCheck
Como mencionaron otros , Valgrind no es bueno para resolver este tipo de problema.
Tiene una herramienta experimental llamada SGCheck :
Así que no me sorprendió mucho cuando no encontró el error:
Aparentemente, el mensaje de error debería verse así: Valgrind falta error
GDB
Una observación importante es que si ejecuta el programa a través de GDB, o examina el
core
archivo después del hecho:entonces, como vimos en el ensamblaje, GDB debería indicarle el final de la función que realizó la verificación canaria:
Y por lo tanto, el problema es probable en una de las llamadas que realizó esta función.
A continuación, tratamos de determinar la llamada fallida exacta al dar un solo paso adelante justo después de configurar el canario:
y mirando la dirección:
Ahora, esto nos deja en la instrucción ofensiva correcta:
len = 5
yi = 4
, en este caso particular, nos indicó la línea culpable 12.Sin embargo, la traza inversa está dañada y contiene algo de basura. Una traza inversa correcta se vería así:
entonces tal vez esto podría corromper la pila y evitar que veas el rastro.
Además, este método requiere saber cuál es la última llamada de la función de verificación canaria; de lo contrario, tendrá falsos positivos, lo que no siempre será factible, a menos que utilice la depuración inversa .
fuente
Mire la siguiente situación:
Cuando deshabilité el protector de aplastamiento de la pila, no se detectaron errores, lo que debería haber sucedido cuando usé "./a.out wepassssssssssssssssss"
Entonces, para responder a su pregunta anterior, se mostró el mensaje "** stack smashing detectado: xxx" porque su protector de stack smashing estaba activo y descubrió que hay un desbordamiento de pila en su programa.
Solo averigua dónde ocurre eso y arréglalo.
fuente
Podrías intentar depurar el problema usando valgrind :
fuente
Significa que escribió en algunas variables en la pila de manera ilegal, probablemente como resultado de un desbordamiento de Buffer .
fuente
Un escenario sería en el siguiente ejemplo:
En este programa, puede invertir una Cadena o una parte de la cadena si, por ejemplo, llama
reverse()
con algo como esto:Si decide pasar la longitud de la matriz de esta manera:
Funciona bien también.
Pero cuando haces esto:
Obtienes:
Y esto sucede porque en el primer código, la longitud de
arr
se verifica dentro de larevSTR()
cual está bien, pero en el segundo código donde pasa la longitud:la longitud ahora es más larga que la longitud real que pasas cuando dices
arr + 2
.Longitud de
strlen ( arr + 2 )
! =strlen ( arr )
.fuente
gets
yscrcpy
. Me pregunto si podríamos minimizar si es más. Yo al menos deshacerse destring.h
consize_t len = sizeof( arr );
. Probado en gcc 6.4, Ubuntu 16.04. También daría el ejemplo erróneo con elarr + 2
fin de minimizar el pegado de copias.Las corrupciones de la pila usualmente causadas por desbordamientos del búfer. Puedes defenderte de ellos programando a la defensiva.
Siempre que acceda a una matriz, coloque una aserción antes para asegurarse de que el acceso no esté fuera de los límites. Por ejemplo:
Esto te hace pensar en los límites de la matriz y también te hace pensar en agregar pruebas para activarlas si es posible. Si algunas de estas afirmaciones pueden fallar durante el uso normal, conviértalas en algo normal
if
.fuente
Recibí este error mientras usaba malloc () para asignar algo de memoria a una estructura * después de gastar algo de esto en la depuración del código, finalmente utilicé la función free () para liberar la memoria asignada y posteriormente desapareció el mensaje de error :)
fuente
Otra fuente de aplastamiento de la pila es el uso (incorrecto) de en
vfork()
lugar defork()
.Acabo de depurar un caso de esto, donde el proceso secundario no pudo
execve()
ejecutar el ejecutable de destino y devolvió un código de error en lugar de llamar_exit()
.Debido a que
vfork()
había engendrado a ese hijo, regresó mientras aún se estaba ejecutando dentro del espacio de proceso del padre, no solo corrompiendo la pila del padre, sino que causó que dos conjuntos dispares de diagnósticos se imprimieran mediante código "aguas abajo".Cambiar
vfork()
parafork()
solucionar ambos problemas, al igual que cambiar lareturn
declaración del niño a_exit()
cambio.Pero dado que el código secundario precede a la
execve()
llamada con llamadas a otras rutinas (para establecer el uid / gid, en este caso particular), técnicamente no cumple con los requisitosvfork()
, por lo que cambiarlo para usarlofork()
es correcto aquí.(Tenga en cuenta que la
return
declaración problemática en realidad no estaba codificada como tal; en cambio, se invocó una macro y esa macro decidió si se basaba_exit()
o noreturn
en una variable global. Por lo tanto, no era obvio de inmediato que el código secundario no era conforme paravfork()
uso. )Para más información, ver:
La diferencia entre fork (), vfork (), exec () y clone ()
fuente