в X86 - регистры сегмента linux используются для проверки переполнения буфера [см. Фрагмент кода ниже, который определил несколько массивов символов в стеке]:
static void
printint(int xx, int base, int sgn)
{
char digits[] = "0123456789ABCDEF";
char buf[16];
int i, neg;
uint x;
neg = 0;
if(sgn && xx < 0){
neg = 1;
x = -xx;
} else {
x = xx;
}
i = 0;
do{
buf[i++] = digits[x % base];
}while((x /= base) != 0);
if(neg)
buf[i++] = '-';
while(--i >= 0)
my_putc(buf[i]);
}
Теперь, если мы увидим дизассемблирование кода, сгенерированного gcc кода.
Дамп кода ассемблера для функции printint:
0x00000000004005a6 <+0>: push %rbp
0x00000000004005a7 <+1>: mov %rsp,%rbp
0x00000000004005aa <+4>: sub $0x50,%rsp
0x00000000004005ae <+8>: mov %edi,-0x44(%rbp)
0x00000000004005b1 <+11>: mov %esi,-0x48(%rbp)
0x00000000004005b4 <+14>: mov %edx,-0x4c(%rbp)
0x00000000004005b7 <+17>: mov %fs:0x28,%rax ------> obtaining an 8 byte guard from based on a fixed offset from fs segment register [from the descriptor base in the corresponding gdt entry]
0x00000000004005c0 <+26>: mov %rax,-0x8(%rbp) -----> pushing it as the first local variable on to stack
0x00000000004005c4 <+30>: xor %eax,%eax
0x00000000004005c6 <+32>: movl $0x33323130,-0x20(%rbp)
0x00000000004005cd <+39>: movl $0x37363534,-0x1c(%rbp)
0x00000000004005d4 <+46>: movl $0x42413938,-0x18(%rbp)
0x00000000004005db <+53>: movl $0x46454443,-0x14(%rbp)
...
...
// function end
0x0000000000400686 <+224>: jns 0x40066a <printint+196>
0x0000000000400688 <+226>: mov -0x8(%rbp),%rax -------> verifying if the stack was smashed
0x000000000040068c <+230>: xor %fs:0x28,%rax --> checking the value on stack is matching the original one based on fs
0x0000000000400695 <+239>: je 0x40069c <printint+246>
0x0000000000400697 <+241>: callq 0x400460 <__stack_chk_fail@plt>
0x000000000040069c <+246>: leaveq
0x000000000040069d <+247>: retq
Теперь, если мы удалим основанные на стеке массивы символов из этой функции, gcc не сгенерирует эту защитную проверку.
Я видел то же самое, сгенерированное gcc даже для модулей ядра. В основном я видел сбой при загрузке некоторого кода ядра, и это происходило с виртуальным адресом 0x28. Позже я подумал, что думал, что правильно инициализировал указатель стека и правильно загрузил программу, у меня нет нужных записей в gdt, которые бы переводили смещение на основе fs в действительный виртуальный адрес.
Однако в случае кода ядра это просто игнорировалось, ошибка вместо перехода к чему-то вроде __stack_chk_fail @ plt>.
Соответствующей опцией компилятора, которая добавляет эту защиту в gcc, является -fstack-protector. Я думаю, что это включено по умолчанию, которое компилирует пользовательское приложение.
Для ядра мы можем включить этот флаг gcc через опцию config CC_STACKPROTECTOR.
config CC_STACKPROTECTOR
699 bool "Enable -fstack-protector buffer overflow detection (EXPERIMENTAL)"
700 depends on SUPERH32
701 help
702 This option turns on the -fstack-protector GCC feature. This
703 feature puts, at the beginning of functions, a canary value on
704 the stack just before the return address, and validates
705 the value just before actually returning. Stack based buffer
706 overflows (that need to overwrite this return address) now also
707 overwrite the canary, which gets detected and the attack is then
708 neutralized via a kernel panic.
709
710 This feature requires gcc version 4.2 or above.
Соответствующий файл ядра, где этот gs / fs - это linux / arch / x86 / include / asm / stackprotector.h