Improve Protection against Stack Buffer Overflows
Much like its predecessor, stack-protector, stack-protector-strong protects against stack buffer overflows, but additionally provides coverage for more array types, as the original only protected character arrays. stack-protector-strong was implemented by Han Shen and added to the GCC 4.9 compiler.
Android 7.0 kernel Security Update introducesstack-protector-strong
.
Earlier is-fstack-protector
And-fstack-protector-all
Option, but they both have disadvantages. The Chrome OS compiler team visually tested obsessive-compulsive disorder and designedstack-protector-strong
.
-Fstack-disadvantages of Protector
Only functions that use a char array of> = 8 bytes (-Param = SSP-buffer-size = N, default n = 8) provide protection, so the protection capability is limited.
-Fstack-protector-all disadvantages
All functions will be added for detection. 1) it will increase the program size. 2) it will occupy the stack space, especially when the kernel stack space is fixed. This is not a good design.
-Fstack-protector-strong
Let's take a look.stack-protector-strong
Option, which has its filtering principle for whether to add canary to the function. In summary, there are several:
- When the local variable's ** address ** is used as part of the right value of the value assignment expression, or as a function parameter
- When the local variable is an array (or contains the Union type of the array), regardless of the array size and type
- Use the Register Type local variable (*) (uses register local variables)
3rd points failed to be verified. What may be wrong?
Through the assembly code, compare:
[email protected]:~/exploits/test$ cat register.c #include <stdio.h>void c (long a){ printf ("%ld\n", a);}/* local variable’s address used as part of function argument */int a () { int a = 10; c((long)&a);}int b () { register int *foo asm ("r12");}/* regardless of array length */void d () { char str[2] = {‘A‘};}/* regardless of array type */void e () { int a[10]; int i; for (i = 0; i < 10; i++) a[i] = ‘A‘;}/* local variable’s address used as part of the right hand side of an assignment */void f (int *b) { int a = 10; b = &a;}int main (){}
stack-protector-strong
The compilation result is as follows:
201710000004005ca <a >:# the local variable address is used as the function parameter 4005ca: 55 push % RBP 4005cb: 48 89 E5 mov % RSP, % RBP 4005ce: 48 83 EC 10 sub $0x10, % RSP 4005d2: 64 48 8B 04 25 28 00 mov % FS: 0x28, % Rax 4005d9: 00 00 4005db: 48 89 45 F8 mov % rax,-0x8 (% RBP) 4005df: 31 C0 XOR % eax, % eax 4005e1: C7 45 F4 0a 00 00 00 movl $ 0xa, -0xc (% RBP) 4005e8: 48 8d 45 F4 lea-0xc (% RBP), % Rax 4005ec: 48 89 C7 mov % rax, % RDI 4005ef: e8 B2 FF callq 4005a6 <C> 4005f4: 48 8B 55 F8 mov-0x8 (% RBP), % RDX 4005f8: 64 48 33 14 25 28 00 XOR % FS: 0x28, % RDX 4005ff: 00 00 400601: 74 05 je 400608 <A + 0x3e> 400603: E8 68 Fe FF callq 400470 <[email protected]> 400608: C9 leaveq 400609: c3 retq 000000000040060a <B >:# when the register type variable is used, why? 40060a: 55 push % RBP 40060b: 48 89 E5 mov % RSP, % RBP 40060e: 5D pop % RBP 40060f: C3 retq 0000000000400610 <D >:# the array length is 2 400610: 55 push % RBP 400611: 48 89 E5 mov % RSP, % RBP 400614: 48 83 EC 10 sub $0x10, % RSP 400618: 64 48 8B 04 25 28 00 mov % FS: 0x28, % Rax 40061f: 00 00 400621: 48 89 45 F8 mov % rax,-0x8 (% RBP) 400625: 31 C0 XOR % eax, % eax 400627: 66 C7 45 F0 00 00 movw $0x0,-0x10 (% RBP) 40062d: c6 45 F0 41 movb $0x41,-0x10 (% RBP) 400631: 48 8B 45 F8 mov-0x8 (% RBP), % Rax 400635: 64 48 33 04 25 28 00 XOR % FS: 0x28, % Rax 40063c: 00 00 40063e: 74 05 je 400645 <D + 0x35> 400640: e8 2B Fe FF callq 400470 <[email protected]> 400645: C9 leaveq 400646: C3 retq 0000000000400647 <e >:# the array type is int 400647: 55 push % RBP 400648: 48 89 E5 mov % RSP, % RBP 40064b: 48 83 EC 40 sub $0x40, % RSP 40064f: 64 48 8B 04 25 28 00 mov % FS: 0x28, % Rax 400656: 00 00 400658: 48 89 45 F8 mov % rax,-0x8 (% RBP) 40065c: 31 C0 XOR % eax, % eax 40065e: c7 45 CC 00 00 00 00 movl $0x0,-0x34 (% RBP) 400665: EB 11 JMP 400678 <e + 0x31> 400667: 8b 45 CC mov-0x34 (% RBP), % eax 40066a: 48 98 cltq 40066c: C7 44 85 D0 41 00 00 movl $0x41, -0x30 (% RBP, % rax, 4) 400673: 00 400674: 83 45 CC 01 addl $0x1,-0x34 (% RBP) 400678: 83 7d CC 09 CMPL $0x9,-0x34 (% RBP) 40067c: 7E E9 jle 400667 <e + 0x20> 40067e: 48 8B 45 F8 mov-0x8 (% RBP), % Rax 400682: 64 48 33 04 25 28 00 XOR % FS: 0x28, % Rax 400689: 00 00 40068b: 74 05 je 400692 <e + 0x4b> 40068d: E8 de fd ff callq 400470 <[email protected]> 400692: C9 leaveq 400693: c3 retq 0000000000400694 <F >:# the local variable is part of the left value of the value assignment expression. 400694: 55 push % RBP 400695: 48 89 E5 mov % RSP, % RBP 400698: 48 83 EC 20 sub $0x20, % RSP 40069c: 48 89 7d E8 mov % RDI,-0x18 (% RBP) 4006a0: 64 48 8B 04 25 28 00 mov % FS: 0x28, % Rax 4006a7: 00 00 4006a9: 48 89 45 F8 mov % rax,-0x8 (% RBP) 4006ad: 31 C0 XOR % eax, % eax 4006af: C7 45 F4 0a 00 00 00 movl $ 0xa,-0xc (% RBP) 4006b6: 48 8d 45 F4 lea-0xc (% RBP), % Rax 4006ba: 48 89 45 E8 mov % rax,-0x18 (% RBP) 4006be: 48 8B 45 F8 mov-0x8 (% RBP), % Rax 4006c2: 64 48 33 04 25 28 00 XOR % FS: 0x28, % Rax 4006c9: 00 00 4006cb: 74 05 je 4006d2 <F + 0x3e> 4006cd: E8 9e fd ff callq 400470 <[email protected]> 4006d2: C9 leaveq 4006d3: C3 retq
By default, the-fstack-Protector option is used during compilation. The result is as follows:
000000000040055a <a>: 40055a: 55 push %rbp 40055b: 48 89 e5 mov %rsp,%rbp 40055e: 48 83 ec 10 sub $0x10,%rsp 400562: c7 45 fc 0a 00 00 00 movl $0xa,-0x4(%rbp) 400569: 48 8d 45 fc lea -0x4(%rbp),%rax 40056d: 48 89 c7 mov %rax,%rdi 400570: e8 c1 ff ff ff callq 400536 <c> 400575: c9 leaveq 400576: c3 retq 0000000000400577 <b>: 400577: 55 push %rbp 400578: 48 89 e5 mov %rsp,%rbp 40057b: 5d pop %rbp 40057c: c3 retq 000000000040057d <d>: 40057d: 55 push %rbp 40057e: 48 89 e5 mov %rsp,%rbp 400581: 66 c7 45 f0 00 00 movw $0x0,-0x10(%rbp) 400587: c6 45 f0 41 movb $0x41,-0x10(%rbp) 40058b: 5d pop %rbp 40058c: c3 retq 000000000040058d <e>: 40058d: 55 push %rbp 40058e: 48 89 e5 mov %rsp,%rbp 400591: c7 45 cc 00 00 00 00 movl $0x0,-0x34(%rbp) 400598: eb 11 jmp 4005ab <e+0x1e> 40059a: 8b 45 cc mov -0x34(%rbp),%eax 40059d: 48 98 cltq 40059f: c7 44 85 d0 41 00 00 movl $0x41,-0x30(%rbp,%rax,4) 4005a6: 00 4005a7: 83 45 cc 01 addl $0x1,-0x34(%rbp) 4005ab: 83 7d cc 09 cmpl $0x9,-0x34(%rbp) 4005af: 7e e9 jle 40059a <e+0xd> 4005b1: 5d pop %rbp 4005b2: c3 retq 00000000004005b3 <f>: 4005b3: 55 push %rbp 4005b4: 48 89 e5 mov %rsp,%rbp 4005b7: 48 89 7d e8 mov %rdi,-0x18(%rbp) 4005bb: c7 45 fc 0a 00 00 00 movl $0xa,-0x4(%rbp) 4005c2: 48 8d 45 fc lea -0x4(%rbp),%rax 4005c6: 48 89 45 e8 mov %rax,-0x18(%rbp) 4005ca: 5d pop %rbp 4005cb: c3 retq
In the above cases, Canary is not inserted in the function. Observe carefully-fstack-protector-stong
Protected objects may be used to execute arbitrary code. For example, if an object is assigned a value or a function parameter is a function pointer, the modified value will execute arbitrary code.
Canary impact on the system
From the Linux kernel 3.14, a new compilation option config_cc_stackprotector_strong is added for-fstack-protector-strong. The original option config_cc_stackprotector (corresponding to-fstack-protector) is changed to config_cc_stackprotector_regular.
The following is the kernel comparison data compiled by the default x86_64 platform.
Compilation options |
Code segment size (in bytes) |
Number of protected functions/total number of functions |
No stack Protector |
11430641 |
0/36110 |
Config_cc_stackprotector_regular |
11468490 (more than 0.33%) |
1015/36110 (2.81%) |
Config_cc_stackprotector_strong |
11692790 (more than 2.24%) |
7401/36110 (20.5%) |
It can be seen that compared to-fstack-protector-all, 20.5% is already good, and there is a good compromise between performance and security.
Reference: https://outflux.net/blog/archives/2014/01/27/fstack-protector-strong/
Stack-protector-strong