Почему "обнаружение разрушения стека" не появляется сразу после разрушения?

Я понимаю, что подразумевается под "обнаружение разрушения стека". Здесь уже есть много вопросов по этому поводу. Но я не нашел ответа на следующий вопрос. Возьми код С

int main(int argc, char **args) {
   char puffer[5];
   strcpy(puffer, *++args);
   printf("%s\n",puffer);
   return EXIT_SUCCESS;
}

Когда я скомпилирую с gcc 4.8.1 в Ubuntu 13.10 ./buffer 123456789 повышает ожидаемую ошибку stack smashing detected, Но почему ./buffer 12345678 поднять без ошибок? Я ожидаю, что каждая строка с более чем 5 символами должна вызывать ошибку.

2 ответа

Решение

Теоретически вы правы и именно так и должны себя вести. Если ваша программа использует более 5 байтов, это может привести к неопределенному поведению. Но часто указатель стека выравнивается по определенным границам по разным причинам производительности. Выравнивание варьируется от архитектуры к архитектуре. Следовательно, вы не видите эту проблему для каждого входа, который больше 5.

Разборка вашей программы отображается ниже. Проверьте sub $0x20,%rsp инструкция, которая выделяет 16 байт памяти в стеке для этой функции.

(gdb) disassemble main
Dump of assembler code for function main(int, char**):
   0x00000000004008b0 <+0>: push   %rbp
   0x00000000004008b1 <+1>: mov    %rsp,%rbp
=> 0x00000000004008b4 <+4>: sub    $0x20,%rsp
   0x00000000004008b8 <+8>: mov    %edi,-0x14(%rbp)
   0x00000000004008bb <+11>:    mov    %rsi,-0x20(%rbp) 
  0x00000000004008bf <+15>: mov    %fs:0x28,%rax
   0x00000000004008c8 <+24>:    mov    %rax,-0x8(%rbp)
   0x00000000004008cc <+28>:    xor    %eax,%eax
   0x00000000004008ce <+30>:    addq   $0x8,-0x20(%rbp)
   0x00000000004008d3 <+35>:    mov    -0x20(%rbp),%rax
   0x00000000004008d7 <+39>:    mov    (%rax),%rdx
   0x00000000004008da <+42>:    lea    -0x10(%rbp),%rax
   0x00000000004008de <+46>:    mov    %rdx,%rsi
   0x00000000004008e1 <+49>:    mov    %rax,%rdi
   0x00000000004008e4 <+52>:    callq  0x400770 <strcpy@plt>
   0x00000000004008e9 <+57>:    lea    -0x10(%rbp),%rax
   0x00000000004008ed <+61>:    mov    %rax,%rdi
   0x00000000004008f0 <+64>:    callq  0x400710 <puts@plt>
   0x00000000004008f5 <+69>:    mov    $0x0,%eax
   0x00000000004008fa <+74>:    mov    -0x8(%rbp),%rcx
   0x00000000004008fe <+78>:    xor    %fs:0x28,%rcx
   0x0000000000400907 <+87>:    je     0x400918 <main(int, char**)+104>
   0x0000000000400909 <+89>:    jmp    0x400913 <main(int, char**)+99>
   0x000000000040090b <+91>:    mov    %rax,%rdi
   0x000000000040090e <+94>:    callq  0x400790 <_Unwind_Resume@plt>
   0x0000000000400913 <+99>:    callq  0x400760 <__stack_chk_fail@plt>
   0x0000000000400918 <+104>:   leaveq 
   0x0000000000400919 <+105>:   retq   

Существуют различные средства защиты от разрушения стека, реализованные компиляторами, но обычно для предотвращения использования переполнения стека канарейка помещается прямо перед указателем сохраненного кадра и адресом возврата. Канарейка здесь для защиты перезаписи сохраненного указателя кадра и адреса возврата. Здесь в основном есть некоторый отступ, вставленный компилятором между концом буфера и адресом указателя кадра / адреса возврата.

Другие вопросы по тегам