Почему "обнаружение разрушения стека" не появляется сразу после разрушения?
Я понимаю, что подразумевается под "обнаружение разрушения стека". Здесь уже есть много вопросов по этому поводу. Но я не нашел ответа на следующий вопрос. Возьми код С
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
Существуют различные средства защиты от разрушения стека, реализованные компиляторами, но обычно для предотвращения использования переполнения стека канарейка помещается прямо перед указателем сохраненного кадра и адресом возврата. Канарейка здесь для защиты перезаписи сохраненного указателя кадра и адреса возврата. Здесь в основном есть некоторый отступ, вставленный компилятором между концом буфера и адресом указателя кадра / адреса возврата.