LoginSignup
1
2

More than 5 years have passed since last update.

Stack overflowを理解する

Last updated at Posted at 2017-09-23

はじめに

Stack overflowについて説明します。
gdbを使って理解します。

Stackとは

stackにはレジスタ、引数、ローカル変数、戻り値、リターンアドレスがおかれます。
例で説明します。

test.c
int main(int argc, char* argv[])
{
    return 0;
}
console
$ gcc -O0 test.c
$ gdb a.out
(gdb) disas main
Dump of assembler code for function main:
   0x00000000004004ed <+0>: push   %rbp
   0x00000000004004ee <+1>: mov    %rsp,%rbp
   0x00000000004004f1 <+4>: mov    %edi,-0x4(%rbp)
   0x00000000004004f4 <+7>: mov    %rsi,-0x10(%rbp)
   0x00000000004004f8 <+11>:    mov    $0x0,%eax
   0x00000000004004fd <+16>:    pop    %rbp
   0x00000000004004fe <+17>:    retq   
End of assembler dump.

push命令を実行するとstackに%rbpがおかれてrspがデクリメントされます。

push %rbp

次の命令はrsp=rbpをベースとして-0x4, -0x10にediとrsiを書き込みます。
rspは変更しませんがstackにデータを書き込みます。
ediとrsiはmain関数の引数argcとargvです。

mov %edi,-0x4(%rbp)
mov %rsi,-0x10(%rbp)

pop命令を実行するとstackのデータをrbpに読み出して、rspをインクリメントします。

pop %rbp

ローカル変数はstackにおかれます。次の例で説明します。

test.c
int main(int argc, char* argv[])
{
    int v[10];
    return 0;
}
console
$ gcc -O0 test.c
$ gdb a.out
(gdb) disas main
Dump of assembler code for function main:
   0x00000000004004ed <+0>: push   %rbp
   0x00000000004004ee <+1>: mov    %rsp,%rbp
   0x00000000004004f1 <+4>: mov    %edi,-0x34(%rbp)
   0x00000000004004f4 <+7>: mov    %rsi,-0x40(%rbp)
   0x00000000004004f8 <+11>:    mov    $0x0,%eax
   0x00000000004004fd <+16>:    pop    %rbp
   0x00000000004004fe <+17>:    retq   
End of assembler dump.

ediを-0x34 = -52 しています。

mov %edi,-0x34(%rbp)

rbpのサイズは8です。ediのサイズは4です。 v[10]は40です。合計で52となります。
-8 ~ -(48-1)の範囲をv[10]として使用します。
このようにrspを操作しないでstackを暗黙的に確保することがあります。

stack address / size の 取得 / 設定

stack address / size は pthread_attr_getstackで取得します。
取得できるstack addressはアクセス可能な最小のアドレスです。

stack size は pthread_attr_setstackで設定します。最小サイズは16kです。

test.c
#include <stdio.h>
#include <limits.h>
#define _GNU_SOURCE
#include <pthread.h>
#define NAMELEN 16

void get_stack_info()
{
    void *stackaddr;
    size_t stacksize, guardsize;
    char name[NAMELEN] = {0};
    pthread_attr_t attr;
    pthread_t self = pthread_self();
    pthread_getname_np(self, name);
    pthread_getattr_np(self, &attr);
    pthread_attr_getstack(&attr, &stackaddr, &stacksize);
    pthread_attr_getguardsize(&attr, &guardsize);
    printf("name=%.4s range=[%p-%p] size=%zu guard size=%zu\n",
        name, stackaddr, stackaddr+stacksize-1, stacksize, guardsize);
}

void *start_routine(void* arg)
{
    get_stack_info();
    return 0;
}

int main(int argc, char* argv[])
{
    int ret;
    pthread_t thread;
    pthread_attr_t attr;
    pthread_attr_init(&attr);
    ret = pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN /* size_t stacksize */);
    if (ret != 0) {
        printf("error pthread_attr_setstacksize %d\n", ret);
        return -1;
    }
    pthread_create(&thread, &attr, start_routine, 0 /* void * arg */);
    pthread_join(thread, 0 /* void **thread_return */);
    return 0;
}
console
$ gcc -g -O0 test.c -lpthread
$ ./a.out 
name=a.ou range=[0x7f5597059000-0x7f559705cfff] size=16384 guard size=4096

stackは低位アドレスに成長します。
guardはread / write禁止領域です。アクセスするとSegmentation faultが発生します。
アクセス可能なアドレス範囲は[stackaddr + guard_size, stackaddr + stacksize - 1]です。

image.png

stack 使用量 チェック

gccの"-fstack-usage"オプションを利用することで静的にstack使用量を確認できます。
具体例で説明します。

test.c
int main(int argc, char* argv[])
{
    char buf[512];
    return 0;
}
console
$ gcc -fstack-usage test.c && cat test.su
test.c:1:5:main 560 static

560 byteのstackを使用していることがわかります。

bufにstaticをつけてみます。

test.c
int main(int argc, char* argv[])
{
    static char buf[512];
    return 0;
}

16 byteに減っています。

console
$ gcc -fstack-usage test.c && cat test.su
test.c:1:5:main 16  static

stack overflowが起こるケース

次のケースでstack overflowが起きる可能性があります。

  • 深いcall stack , 再帰呼び出し
  • サイズの大きなlocal変数
  • サイズの大きな値渡し
1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2