¡Vamos a probarlo! Aquí hay un programa de ejemplo muy simple.
int test(int a)
{
return a;
}
Compílela con GCC e intercepte la compilación en la etapa de ensamblaje. (El indicador -S
hará esto). Cambie el nombre del archivo de ensamblaje (para que no se sobrescriba) y compile nuevamente, esta vez también agregando los indicadores -fstack-protector-all
y -mstack-protector-guard=global
. La primera bandera permite apilar canarios para todas las funciones, la segunda selecciona un canario global en lugar de un hilo local. (El valor predeterminado de subprocesos locales es probablemente más útil en la práctica, pero el ensamblaje para la versión global es más fácil de entender).
Al comparar los dos archivos de ensamblaje generados, detectamos la siguiente adición (los comentarios son míos).
movl %edi, -20(%rbp) ; save function parameter onto stack (unrelated to canary)
movq __stack_chk_guard(%rip), %rax ; load magic value into RAX register
movq %rax, -8(%rbp) ; save RAX register onto stack (place the canary)
movl -20(%rbp), %eax ; load function parameter into EAX register for return (unrelated to canary)
movq -8(%rbp), %rcx ; load canary value into RCX register
movq __stack_chk_guard(%rip), %rdx ; load magic value into RDX register
cmpq %rdx, %rcx ; compare canary value to expected value
je .L3 ; if they are the same, jump to label .L3 (continue)
call __stack_chk_fail ; otherwise (stack corruption detected), call the handler
.L3:
leave
Podemos ver que el canario se maneja en los registros RAX, RCX y RDX que tienen 64 bits de ancho. (Sus homólogos de 32 bits se llamarían EAX, EBX y EDX. Las versiones de 16 bits se denominan AX, BX y CX. Las variantes de 8 bits AL, BL y CL.) Otra pista es que las operaciones para almacenar, cargar y comparar canary (MOVQ y CMPQ) tienen un sufijo 'Q' que identifica una instrucción de 64 bits. (Las instrucciones de 32 bits tienen un sufijo 'L', las instrucciones de 16 bits a 'W' y las versiones de 8 bits a 'B'.)
Por lo tanto, llegamos a la conclusión de que el canario es un valor de 64 bits, lo que tiene sentido en una arquitectura de 64 bits (x86_64 GNU / Linux en mi caso). Espero que siempre usen el tamaño de palabra nativo ya que tiene más sentido para mí. Puedes probar el mismo experimento en tus máquinas y ver lo que obtendrás.