|
CC_STACKPROTECTOR·ÀÖ¹ÄÚºËstackÒç³ö²¹¶¡·ÖÎö
by wzt <wzt.wzt@gmail.com>
CC_STACKPROTECT²¹¶¡ÊÇTejun HeoÔÚ09Äê¸øÖ÷ÏßkernelÌá½»µÄÒ»¸öÓÃÀ´·ÀÖ¹Äں˶ÑÕ»Òç³öµÄ²¹¶¡¡£Ä¬ÈϵÄconfigÊǽ«Õâ¸öÑ¡Ïî¹Ø±ÕµÄ£¬¿ÉÒÔÔÚ±àÒëÄں˵Äʱºò£¬ ÐÞ¸Ä.configÎļþΪCONFIG_CC_STACKPROTECTOR=yÀ´ÆôÓá£Î´À´·ÉÌìÄں˿ÉÒÔ½«Õâ¸öÑ¡ÏÆôÀ´·ÀÖ¹ÀûÓÃÄÚºËstackÒç³öµÄ0day¹¥»÷¡£ Õâ¸ö²¹¶¡µÄ·ÀÒç³öÔÀíÊÇ£º ÔÚ½ø³ÌÆô¶¯µÄʱºò£¬ ÔÚÿ¸öbufferµÄºóÃæ·ÅÖÃÒ»¸öÔ¤ÏÈÉèÖúõÄstack canary£¬Äã¿ÉÒÔ °ÑËüÀí½â³ÉÒ»¸öÉÚ±ø£¬ µ±buffer·¢Éú»º³åÇøÒç³öµÄʱºò£¬ ¿Ï¶¨»áÆÆ»µstack canaryµÄÖµ£¬ µ±stack canaryµÄÖµ±»ÆÆ»µµÄʱºò£¬ Äں˾ͻáÖ±½Óµ±»ú¡£ÄÇôÊÇÔõôÅжÏstack canary ±»¸²¸ÇÁËÄØ£¿ ÆäʵÕâ¸öÊÂÇéÊÇgccÀ´×öµÄ£¬ÄÚºËÔÚ±àÒëµÄʱºò¸øgcc¼ÓÁ˸ö-fstack-protector²ÎÊý£¬ ÎÒÃÇÏÈÀ´Ñо¿ÏÂÕâ¸ö²ÎÊýÊÇ×öʲôÓõġ£
ÏÈд¸ö¼òµ¥µÄÓÐÒç³öµÄ³ÌÐò£º [wzt@localhost csaw]$ cat test.c #include <stdio.h> #include <stdlib.h>
void test(void) { char buff[64];
memset(buff, 0x41, 128); //Ïò64´óСµÄbuffer¿½±´128×Ö½Ú£¬ ¿Ï¶¨»á·¢Éú»º³åÇøÒç³ö¡£ }
int main(void) { test();
return 0; } [wzt@localhost csaw]$ gcc -o test test.c [wzt@localhost csaw]$ ./test ¶Î´íÎó
·´»ã±à¿´¿´£º [wzt@localhost csaw]$ objdump -d test > hex
08048384 <test>: 8048384: 55 push %ebp 8048385: 89 e5 mov %esp,%ebp 8048387: 83 ec 58 sub $0x58,%esp 804838a: c7 44 24 08 80 00 00 movl $0x80,0x8(%esp) 8048391: 00 8048392: c7 44 24 04 41 00 00 movl $0x41,0x4(%esp) 8048399: 00 804839a: 8d 45 c0 lea 0xffffffc0(%ebp),%eax 804839d: 89 04 24 mov %eax,(%esp) 80483a0: e8 e3 fe ff ff call 8048288 <memset@plt> 80483a5: c9 leave 80483a6: c3 ret
ûʲôÌرðµÄ£¬ÎÒÃÇÔÚ¼ÓÉÏ-fstack-protector²ÎÊý¿´¿´£º [wzt@localhost csaw]$ gcc -o test test.c -fstack-protector [wzt@localhost csaw]$ ./test *** stack smashing detected ***: ./test terminated ÒÑ·ÅÆú Õâ´Î³ÌÐò´òÓ¡ÁËÒ»Ìõ¶ÑÕ»±»Òç³öµÄÐÅÏ¢£¬È»ºó¾Í×Ô¶¯Í˳öÁË¡£
ÔÚ·´»ã±à¿´Ï£º [wzt@localhost csaw]$ objdump -d test > hex1
080483d4 <test>: 80483d4: 55 push %ebp 80483d5: 89 e5 mov %esp,%ebp 80483d7: 83 ec 68 sub $0x68,%esp 80483da: 65 a1 14 00 00 00 mov %gs:0x14,%eax 80483e0: 89 45 fc mov %eax,0xfffffffc(%ebp) 80483e3: 31 c0 xor %eax,%eax 80483e5: c7 44 24 08 80 00 00 movl $0x80,0x8(%esp) 80483ec: 00 80483ed: c7 44 24 04 41 00 00 movl $0x41,0x4(%esp) 80483f4: 00 80483f5: 8d 45 bc lea 0xffffffbc(%ebp),%eax 80483f8: 89 04 24 mov %eax,(%esp) 80483fb: e8 cc fe ff ff call 80482cc <memset@plt> 8048400: 8b 45 fc mov 0xfffffffc(%ebp),%eax 8048403: 65 33 05 14 00 00 00 xor %gs:0x14,%eax 804840a: 74 05 je 8048411 <test+0x3d> 804840c: e8 db fe ff ff call 80482ec <__stack_chk_fail@plt> 8048411: c9 leave 8048412: c3 ret
ʹÓÃ-fstack-protector²ÎÊýºó£¬ gccÔÚº¯ÊýµÄ¿ªÍ··ÅÖÃÁ˼¸Ìõ»ã±à´úÂ룺 80483d7: 83 ec 68 sub $0x68,%esp 80483da: 65 a1 14 00 00 00 mov %gs:0x14,%eax 80483e0: 89 45 fc mov %eax,0xfffffffc(%ebp) ½«´úÂë¶ÎgsÆ«ÒÆ0x14ÄÚ´æ´¦µÄÖµ¸³Öµ¸øÁËebp-4£¬ Ò²¾ÍÊǵÚÒ»¸ö±äÁ¿ÖµµÄºóÃæ¡£
ÔÚcallÍêmemesetºó£¬ÓÐÈçÏ»ã±à´úÂ룺 80483fb: e8 cc fe ff ff call 80482cc <memset@plt> 8048400: 8b 45 fc mov 0xfffffffc(%ebp),%eax 8048403: 65 33 05 14 00 00 00 xor %gs:0x14,%eax 804840a: 74 05 je 8048411 <test+0x3d> 804840c: e8 db fe ff ff call 80482ec <__stack_chk_fail@plt> ÔÚmemsetºó£¬gccÒª¼ì²éÕâ¸ö²Ù×÷ÊÇ·ñ·¢ÉúÁ˶ÑÕ»Òç³ö, ½«±£´æÔÚebp-4µÄÕâ¸öÖµÓëÔÀ´µÄÖµ¶Ô±Èһϣ¬ Èç¹û²»Ïàͬ£¬ ˵Ã÷¶ÑÕ»·¢ÉúÁËÒç³ö£¬ÄÇô¾Í»áÖ´ÐÐ__stack_chk_failÕâ¸öº¯Êý£¬ Õâ¸öº¯ÊýÊÇglibcʵÏֵģ¬ ´òÓ¡³öÉÏÃæ¿´µ½µÄÐÅÏ¢£¬ È»ºó½ø³ÌÍ˳ö¡£
´ÓÕâ¸öÀý×ÓÖÐÎÒÃÇ¿ÉÒÔ¿´³ögccʹÓÃÁË-fstack-protector²ÎÊýºó£¬»á×Ô¶¯¼ì²é¶ÑÕ»ÊÇ·ñ·¢ÉúÁËÒç³ö£¬ µ«ÊÇÓÐÒ»¸öÇ°Ìá¾ÍÊÇ ÄÚºËÒª¸øÿ¸ö½ø³ÌÌáÇ°ÉèÖúÃÒ»¸ö¼ì²âÖµ·ÅÖÃÔÚ%gs:0x14λÖô¦£¬ Õâ¸öÖµ³Æ֮Ϊstack canary¡£ËùÒÔÎÒÃÇ¿ÉÒÔ¿´µ½·ÀÖ¹ ¶ÑÕ»Òç³öÊÇÓÉÄں˺Ígcc¹²Í¬À´Íê³ÉµÄ¡£
gccµÄÈÎÎñ¾ÍÊÇ·ÅÖü¸Ìõ»ã±à´úÂ룬 È»ºóºÍ%gs:0x14λÖô¦µÄÖµ½øÐжԱȼ´¿É¡£ Ö÷ÒªÈÎÎñ»¹ÊÇÄÚºËÈçºÎÀ´ÉèÖÃstack canary£¬ Ò²ÊÇ CC_STACKPROTECTOR²¹¶¡ÒªÊµÏÖµÄÄ¿µÄ£¬ ÏÂÃæÎÒÃÇ×ÐϸÀ´¿´ÏÂÕâ¸ö²¹¶¡ÊÇÈçºÎʵÏֵġ£
¼ÈÈ»gccÓ²ÐԹ涨ÁËstack canary±ØÐëÔÚ%gsµÄij¸öÆ«ÒÆλÖô¦£¬ ÄÇôÄÚºËÒ²±ØÐë°´×ÅÕâ¸ö¹æ¶¨À´ÉèÖá£
¶ÔÓÚ32λºÍ64λÄںˣ¬ gs¼Ä´æÆ÷ÓÐ×Ų»Í¬µÄ¹¦ÄÜ¡£
64λÄÚºËgccÒªÇóstack canaryÊÇ·ÅÖÃÔÚgs¶ÎµÄ40Æ«ÒÆ´¦£¬ ²¢ÇÒgs¼Ä´æÆ÷ÔÚÿcpu±äÁ¿ÖÐÊǹ²ÏíµÄ£¬Ã¿cpu±äÁ¿irq_stack_unionµÄ½á¹¹ÈçÏ£º
arch/x86/include/asm/processor.h
union irq_stack_union { char irq_stack[IRQ_STACK_SIZE]; /* * GCC hardcodes the stack canary as %gs:40. Since the * irq_stack is the object at %gs:0, we reserve the bottom * 48 bytes of the irq stack for the canary. */ struct { char gs_base[40]; unsigned long stack_canary; }; };
DECLARE_PER_CPU_FIRST(union irq_stack_union, irq_stack_union); gs_baseÖ»ÊÇÒ»¸ö40×Ö½ÚµÄվλ¿Õ¼ä£¬ stack_canary¾Í½ô°¤Æäºó¡£ ²¢ÇÒÔÚÓ¦ÓóÌÐò½ø³öÄں˵Äʱºò£¬Äں˻áʹÓÃswapgsÖ¸Áî×Ô¶¯¸ü»»gs¼Ä´æÆ÷µÄÄÚÈÝ¡£
32λϾÍÉÔ΢Óе㸴ÔÓÁË¡£ÓÉÓÚijЩ´¦ÀíÆ÷ÔÚ¼ÓÔز»Í¬µÄ¶Î¼Ä´æÆ÷ʱºÜÂý£¬ ËùÒÔÄÚºËʹÓÃfs¶Î¼Ä´æÆ÷Ìæ»»ÁË gs¼Ä´æÆ÷¡£ µ«ÊÇgccÔÚʹÓÃ-fstack-protectorµÄʱºò£¬ »¹ÒªÓõ½gs¶Î¼Ä´æÆ÷£¬ ËùÒÔÄں˻¹Òª¹ÜÀígs¼Ä´æÆ÷£¬ ÎÒÃÇÒª°ÑCONFIG_X86_32_LAZY_GSÑ¡Ïî¹Ø±Õ£¬ gsÒ²Ö»ÔÚ½ø³ÌÇл»µÄʱºò²Å¸Ä±ä¡£ 32λÓÃÿcpu±äÁ¿stack_canary±£´æstack canary¡£
struct stack_canary { char __pad[20]; /* canary at %gs:20 */ unsigned long canary; }; DECLARE_PER_CPU_ALIGNED(struct stack_canary, stack_canary);
ÄÚºËÊÇ´¦ÓÚ±£»¤Ä£Ê½µÄ£¬ Òò´Ëgs¼Ä´æÆ÷¾Í±ä³ÉÁ˱£»¤Ä£Ê½ÏµĶÎÑ¡×Ó£¬ÔÚGDT±íÖÐÒ²ÒªÓÐÏàÓ¦µÄÉèÖãº
diff --git a/arch/x86/include/asm/segment.h b/arch/x86/include/asm/segment.h index 1dc1b51..14e0ed8 100644 (file) --- a/arch/x86/include/asm/segment.h +++ b/arch/x86/include/asm/segment.h @@ -61,7 +61,7 @@ * * 26 - ESPFIX small SS * 27 - per-cpu [ offset to per-cpu data area ] - * 28 - unused + * 28 - stack_canary-20 [ for stack protector ] * 29 - unused * 30 - unused * 31 - TSS for double fault handler @@ -95,6 +95,13 @@ #define __KERNEL_PERCPU 0 #endif +#define GDT_ENTRY_STACK_CANARY (GDT_ENTRY_KERNEL_BASE + 16) +#ifdef CONFIG_CC_STACKPROTECTOR +#define __KERNEL_STACK_CANARY (GDT_ENTRY_STACK_CANARY * 8) +#else +#define __KERNEL_STACK_CANARY 0 +#endif + #define GDT_ENTRY_DOUBLEFAULT_TSS 31
GDT±íÖеĵÚ28¸ö±íÏîÓÃÀ´¶¨Îªstack canaryËùÔڵĶΡ£
#define GDT_STACK_CANARY_INIT \ [GDT_ENTRY_STACK_CANARY] = GDT_ENTRY_INIT(0x4090, 0, 0x18),
GDT_STACK_CANARY_INITÔÚ¸Õ½øÈë±£»¤Ä£Ê½µÄʱºò±»µ÷Ó㬠Õâ¸ö¶ÎÃèÊö·ûÏî±»ÉèÖÃΪ»ùµØַΪ0£¬ ¶Î´óСÉèΪ24£¬ÒòΪֻÔÚ»ùµØַΪ0£¬ Æ«ÒÆΪ0x14´¦·ÅÖÃÒ»¸ö4bytesµÄstack canary£¬ ËùÒÔ24×Ö½ÚÕýºÃ¡£ ²»Àí½âµÄͬѧ¿ÉÒÔ¿´¿´intel±£»¤Ä£Ê½µÄÊֲᣬ ¶Ô×ŶÎÃèÊö·û½á¹¹Ò»¸ö¸ö¿´¾ÍÐÐÁË¡£
ÔÚ½øÈë±£»¤Ä£Ê½ºó£¬ start_kernel()»áµ÷ÓÃboot_init_stack_canary()À´³õʼ»°Ò»¸östack canary¡£ /* * Initialize the stackprotector canary value. * * NOTE: this must only be called from functions that never return, * and it must always be inlined. */ static __always_inline void boot_init_stack_canary(void) { u64 canary; u64 tsc; #ifdef CONFIG_X86_64 BUILD_BUG_ON(offsetof(union irq_stack_union, stack_canary) != 40); #endif /* * We both use the random pool and the current TSC as a source * of randomness. The TSC only matters for very early init, * there it already has some randomness on most systems. Later * on during the bootup the random pool has true entropy too. */ get_random_bytes(&canary, sizeof(canary)); tsc = __native_read_tsc(); canary += tsc + (tsc << 32UL); current->stack_canary = canary; #ifdef CONFIG_X86_64 percpu_write(irq_stack_union.stack_canary, canary); #else percpu_write(stack_canary.canary, canary); #endif } Ëæ»ú³öÁËÒ»¸öÖµ¸³Öµ¸øÿcpu±äÁ¿£¬ 32λÊÇstack_canary, 64λÊÇirq_stack_union¡£
ÄÚºËÔÚ½øÒ»²½³õʼ»¯cpuµÄʱºò£¬»áµ÷ÓÃsetup_stack_canary_segment()À´ÉèÖÃÿ¸öcpuµÄGDTµÄstack canaryÃèÊö·ûÏ
start_kernel()->setup_per_cpu_areas()->setup_stack_canary_segment£º
static inline void setup_stack_canary_segment(int cpu) { #ifdef CONFIG_X86_32 unsigned long canary = (unsigned long)&per_cpu(stack_canary, cpu); struct desc_struct *gdt_table = get_cpu_gdt_table(cpu); struct desc_struct desc;
desc = gdt_table[GDT_ENTRY_STACK_CANARY]; set_desc_base(&desc, canary); write_gdt_entry(gdt_table, GDT_ENTRY_STACK_CANARY, &desc, DESCTYPE_S); #endif }
ÔÚÄں˸սøÈë±£»¤Ä£Ê½µÄʱºò, stack canaryÃèÊö·ûµÄ»ùµØÖ·±»³õʼ»¯Îª0£¬ ÏÖÔÚÔÚcpu³õʼ»¯µÄʱºòÒªÖØÐÂÉèÖÃΪÿcpu±äÁ¿stack_canaryµÄµØÖ·£¬ ¶ø²»ÊDZäÁ¿±£´æµÄÖµ¡£Í¨¹ýÕâЩÉèÖõ±Äں˴úÂëÔÚ·ÃÎÊ%gs:0x14µÄʱºò£¬ ¾Í»á·ÃÎÊstack canry±£´æµÄÖµ¡£×¢Ò⣺setup_stack_canary_segmentÊÇÕë¶Ô32λÄÚºË×öÉèÖ㬠ÒòΪ64λÄÚºËÖеÄirq_stack_unionÊÇÿ¸öcpu¹²ÏíµÄ£¬ ²»ÓÃÕë¶Ôÿ¸öcpuµ¥¶ÀÉèÖᣠȻºó¾Í¿ÉÒÔµ÷ÓÃswitch_to_new_gdt(cpu);À´¼ÓÔØGDT±íºÍ¼ÓÔØgs¼Ä´æÆ÷¡£
¾¹ýÉÏÊö³õʼ»¯¹ý³Ì£¬ÔÚÄں˴úÂëÀï·ÃÎÊ%gs:0x14¾Í¿ÉÒÔ¶¨Î»stack canaryµÄÖµÁË£¬ ÄÇôÿ¸ö½ø³ÌµÄstack canaryÊÇʲôʱºòÉèÖõÄÄØ£¿
ÔÚÄÚºËÆô¶¯Ò»¸ö½ø³ÌµÄʱºò£¬ »á°Ñgs¼Ä´æÆ÷µÄÖµÉèΪ__KERNEL_STACK_CANARY
--- a/arch/x86/kernel/process_32.c +++ b/arch/x86/kernel/process_32.c @@ -212,6 +212,7 @@ int kernel_thread(int (*fn)(void *), void *arg, unsigned long flags) regs.ds = __USER_DS; regs.es = __USER_DS; regs.fs = __KERNEL_PERCPU; + regs.gs = __KERNEL_STACK_CANARY; regs.orig_ax = -1; regs.ip = (unsigned long) kernel_thread_helper; regs.cs = __KERNEL_CS | get_kernel_rpl();
ÄÚºËÔÚforkÒ»¸ö½ø³ÌµÄʱºò£¬ ÓÐÈçϲÙ×÷£º
static struct task_struct *dup_task_struct(struct task_struct *orig) { #ifdef CONFIG_CC_STACKPROTECTOR tsk->stack_canary = get_random_int(); #endif }
Ëæ»ú³õʼ»¯ÁËÒ»¸östack_canary±£´æÔÚtask_struct½á¹¹ÖеÄstack_canary±äÁ¿ÖС£µ±½ø³ÌÔÚÇл»µÄʱºò£¬ ͨ¹ýswitchºê°Ñнø³ÌµÄstack canary±£´æÔÚÿcpu±äÁ¿stack_canaryÖУ¬ µ±Ç°½ø³ÌµÄstack_canaryÒ²±£´æÔÚÒ»¸öÿcpu±äÁ¿ÖУ¬Íê³Éstack canaryµÄÇл»¡£
diff --git a/arch/x86/include/asm/system.h b/arch/x86/include/asm/system.h index 79b98e5..2692ee8 100644 (file) --- a/arch/x86/include/asm/system.h +++ b/arch/x86/include/asm/system.h @@ -23,6 +23,22 @@ struct task_struct *__switch_to(struct task_struct *prev, #ifdef CONFIG_X86_32 +#ifdef CONFIG_CC_STACKPROTECTOR +#define __switch_canary \ + "movl "__percpu_arg([current_task])",%%ebx\n\t" \ + "movl %P[task_canary](%%ebx),%%ebx\n\t" \ + "movl %%ebx,"__percpu_arg([stack_canary])"\n\t" +#define __switch_canary_oparam \ + , [stack_canary] "=m" (per_cpu_var(stack_canary)) +#define __switch_canary_iparam \ + , [current_task] "m" (per_cpu_var(current_task)) \ + , [task_canary] "i" (offsetof(struct task_struct, stack_canary)) +#else /* CC_STACKPROTECTOR */ +#define __switch_canary +#define __switch_canary_oparam +#define __switch_canary_iparam +#endif /* CC_STACKPROTECTOR */ + /* * Saving eflags is important. It switches not only IOPL between tasks, * it also protects other tasks from NT leaking through sysenter etc. @@ -46,6 +62,7 @@ do { \ "pushl %[next_ip]\n\t" /* restore EIP */ \ "jmp __switch_to\n" /* regparm call */ \ "1:\t" \ + __switch_canary \ "popl %%ebp\n\t" /* restore EBP */ \ "popfl\n" /* restore flags */ \ \ @@ -58,6 +75,8 @@ do { \ "=b" (ebx), "=c" (ecx), "=d" (edx), \ "=S" (esi), "=D" (edi) \ \ + __switch_canary_oparam \ + \ /* input parameters: */ \ : [next_sp] "m" (next->thread.sp), \ [next_ip] "m" (next->thread.ip), \ @@ -66,6 +85,8 @@ do { \ [prev] "a" (prev), \ [next] "d" (next) \ \ + __switch_canary_iparam \ + \ : /* reloaded segment registers */ \ "memory"); \ } while (0)
Ç°Ãæ½²¹ýµ±gcc¼ì²âµ½¶ÑÕ»Òç³öµÄʱºò£¬ »áµ÷ÓÃglibcµÄ__stack_chk_failº¯Êý£¬ µ«Êǵ±Äں˶ÑÕ»·¢ÉúÒç³öµÄʱºò£¬ ²»Äܵ÷ÓÃglibcµÄº¯Êý£¬ËùÒÔÄÚºË×Ô¼ºÊµÏÖÁËÒ»¸ö__stack_chk_failº¯Êý£º
kernel/panic.c
#ifdef CONFIG_CC_STACKPROTECTOR
/* * Called when gcc's -fstack-protector feature is used, and * gcc detects corruption of the on-stack canary value */ void __stack_chk_fail(void) { panic("stack-protector: Kernel stack is corrupted in: %p\n", __builtin_return_address(0)); } EXPORT_SYMBOL(__stack_chk_fail);
#endif
µ±Äں˶ÑÕ»·¢ÉúÒç³öµÄʱºò£¬¾Í»áÖ´ÐÐ__stack_chk_failº¯Êý£¬ Äں˵±»ú¡£ Õâ¾ÍÊÇÕâ¸ö²¹¶¡µÄÔÀí£¬²»¶®µÄͬѧÇë²Î¿¼£º http://git.kernel.org/?p=linux/kernel/git/next/linux-next.git;a=commitdiff;h=60a5317ff0f42dd313094b88f809f63041568b08
|