4.2.4.12 スタック破壊検出機能 【Professional版のみ】

スタック破壊検出機能は,-Xstack_protector オプション,-Xstack_protector_all オプションまたは本節で説明する#pragma で実現します。

#pragma stack_protector ( 関数指定 [, 関数指定]... ) 
  関数指定 : 関数名 [ (num=整数値) ]
#pragma no_stack_protector ( 関数名 [, 関数名]... ) 

外側のかっこは省略可能です。

-

関数の入口・出口にスタック破壊検出コードを生成します。スタック破壊検出コードとは次に示す3つの処理を実行するための命令を指します。

(1) 関数の入口で,ローカル変数領域の直前(0xFFFFFFFF番地に向かう方向)に4バイトの領域を確保し, <数値>で指定した値を確保した領域に格納します。

(2) 関数の出口で,<数値>を格納した4バイトの領域が書き換わっていないことをチェックします。

(3) (2)で書き換わっている場合には,スタックが破壊されたとして__stack_chk_fail 関数を呼び出します。

-

<数値>には 0 から 4294967295までの整数値を指定します。<数値>の指定を省略した場合には,コンパイラが自動的に数値を指定します。

-

__stack_chk_fail関数はユーザが定義する必要があり,スタックの破壊検出時に実行する処理を記述します。
__stack_chk_fail関数を定義する際には,次の項目に注意してください。

-

返却値の型はvoid型のみであり,仮引数を持たない関数です。

-

static指定をしないでください。

-

通常の関数のように呼び出すことは禁止します。

-

__stack_chk_fail関数は,オプション-Xstack_protector,-Xstack_protector_allと#pragma stack_protectorによるスタック破壊検出コードの生成の対象にはなりません。

-

関数内ではabort()を呼び出してプログラムを終了させるなど,呼び出し元であるスタックの破壊を検出した関数にリターンしないようにしてください。

-

__stack_chk_fail 関数内で関数を呼び出す場合は,呼び出した先の関数内で再帰的にスタックの破壊を検出しないように注意してください。

-

#pragma no_stack_protector が指定された関数は-Xstack_protector オプション,-Xstack_protector_all オプションに関わらず,スタック破壊検出コードを生成しません。

-

#pragma stack_protector と-Xstack_protector オプション,-Xstack_protector_all オプションが同時に使用された場合は,#pragma 指定が有効になります。

-

同一翻訳単位内で,同じ関数に対して #pragma stack_protector と no_stack_protector を同時に指定した場合はエラーとします。

-

指定した関数が次のいずれかの関数として指定されている場合,エラーとします。
#pragma inline,inline キーワード
#pragma inline_asm

 

-

<Cソース>

#include <stdio.h>
#include <stdlib.h>
 
#pragma stack_protector f1(num=1234)
void f1()   // スタックが破壊されるプログラムの例
{
    volatile char str[10];
    int i;
    for (i = 0; i <= 10; i++){
      str[i] = i; // i=10 の場合にスタックが破壊される
    }
}
 
void __stack_chk_fail(void)
{
    printf("stack is broken!");
    abort(); 
}

-

<出力コード>

_f1:
        .stack _f1 = 16
        add 0xFFFFFFF0, r3
        movea 0x000004D2, r0, r1  ; 指定した<数値> 1234 をスタックの領域へ格納する
        st.w r1, 0x0000000C[r3]
        mov 0x00000000, r2
        br9 .BB.LABEL.1_2
.BB.LABEL.1_1:  ; bb
        movea 0x00000002, r3, r5
        add r2, r5
        st.b r2, 0x00000000[r5]
        add 0x00000001, r2
.BB.LABEL.1_2:  ; bb7
        cmp 0x0000000B, r2
        blt9 .BB.LABEL.1_1
.BB.LABEL.1_3:  ; return
        ld.w 0x0000000C[r3], r1   ; 関数の入口で<数値>を格納した位置からロードし,
        movea 0x000004D2, r0, r12 ; 指定した<数値>1234 と,
        cmp r12, r1               ; 比較する
        bnz9 .BB.LABEL.1_5        ; 異なっている場合には,分岐する
.BB.LABEL.1_4:  ; return
        dispose 0x00000010, 0x00000000, [r31]
.BB.LABEL.1_5:  ; return
        br9 ___stack_chk_fail     ; __stack_chk_failを呼び出す
 
___stack_chk_fail:
        .stack ___stack_chk_fail = 4
        prepare 0x00000001, 0x00000000
        mov #.STR.1, r6
        jarl _printf, r31
        jarl _abort, r31
        dispose 0x00000000, 0x00000001, [r31]