stack-protector-strong

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 的内核安全更新中,引入了 stack-protector-strong

更早的是 -fstack-protector 与 -fstack-protector-all选项,但它们各自都有缺点,Chrome OS 的编译器团队目测有强迫症,设计了 stack-protector-strong

-fstack-protector 的缺点

仅对使用>=8字节(–param=ssp-buffer-size=N, 默认N=8)的char数组的函数提供保护,因此其保护能力有限。

-fstack-protector-all 的缺点

所有函数都会加入检测,1)会增加程序体积,2)占用栈空间,尤其内核栈空间固定的情况;可以认为这都不是好的设计。

-fstack-protector-strong

再来看一下 stack-protector-strong 选项,它对是否在函数中加入canary有其筛选原则,总结起来有几条:

  1. 当本地变量的***地址***,作为赋值表达式右值 的一部分,或作为函数参数
  2. 当本地变量是数组(或包含数组的union 类型),不管数组大小与类型
  3. 使用 register 类型的本地变量(*)(uses register local variables)

第3点未能验证,可能理解有误?

通过汇编代码,对比一下:

zzhiyuan@ubuntu:~/exploits/test$ cat register.c 
#include <stdio.h>

void c (long a)
{
        printf ("%ld
", 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选项编译结果如下:

00000000004005ca <a>:    #本地变量地址作为函数参数
  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 ff 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 ff          callq  400470 <__stack_chk_fail@plt>
  400608:       c9                      leaveq 
  400609:       c3                      retq   

000000000040060a <b>:    #使用register类型变量,Why未印证?
  40060a:       55                      push   %rbp
  40060b:       48 89 e5                mov    %rsp,%rbp
  40060e:       5d                      pop    %rbp
  40060f:       c3                      retq   

0000000000400610 <d>:    #数组长度为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 ff          callq  400470 <__stack_chk_fail@plt>
  400645:       c9                      leaveq 
  400646:       c3                      retq   

0000000000400647 <e>:    #数组类型为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 ff          callq  400470 <__stack_chk_fail@plt>
  400692:       c9                      leaveq 
  400693:       c3                      retq   

0000000000400694 <f>:    #本地变量作为赋值表达式左值的一部分
  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 ff          callq  400470 <__stack_chk_fail@plt>
  4006d2:       c9                      leaveq 
  4006d3:       c3                      retq   

默认编译时,使用的是-fstack-protector选项,其结果如下:

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   

以上几种情况,函数内均未插入canary。仔细观察 -fstack-protector-stong 保护的对象,其实都是有可能被利用来执行任意代码的,比如若被赋值对象,或函数参数是一个函数指针,则修改其值会执行任意代码。

canary 对系统影响

Linux 从3.14内核开始,为-fstack-protector-strong增加了一个新的编译选项CONFIG_CC_STACKPROTECTOR_STRONG,并且原有选项CONFIG_CC_STACKPROTECTOR(对应-fstack-protector)修改为CONFIG_CC_STACKPROTECTOR_REGULAR。

以下是x86_64平台默认选项编译的内核对比数据。

编译选项代码段大小(字节)被保护函数个数 / 函数总数
no stack protector 11430641 0 / 36110
CONFIG_CC_STACKPROTECTOR_REGULAR 11468490 (+0.33%) 1015 / 36110 (2.81%)
CONFIG_CC_STACKPROTECTOR_STRONG 11692790 (+2.24%) 7401 / 36110 (20.5%)

可以看出比起-fstack-protector-all,20.5%已经不错,在性能与安全之间找到了很好的折衷。

参考 :https://outflux.net/blog/archives/2014/01/27/fstack-protector-strong/

原文地址:https://www.cnblogs.com/gm-201705/p/9863958.html