(57)【要約】      （修正有）【課題】バッファーオーバーフローによる不正コードの実行を防ぐ。【解決手段】プログラムを構成する関数からコールグラフを作り１０２、コールグラフ中にループ（再帰呼び出し）があることを検出し１０３、そのループ中に含まれる関数には従来方法によるセキュリティー対策を施し１０８、そのループに含まれない関数は自動変数をグローバル変数又は静的変数へ変換１１０して実行する。【選択図】図１
2007004647.tif 000002
【特許請求の範囲】
【請求項１】
プログラムに含まれる、手続相互の呼び出し関係を作成する第１のステップと、
前記呼び出し関係を用いて、前記プログラムの手続に含まれる関数を、再帰呼び出しを行なう関数と、行なわない関数に分ける第２のステップと、
前記再帰呼び出しを行なわない関数に対して、その関数の自動変数をグローバル変数へ変換する第３のステップと、
前記グローバル変数の名前を変更する第４のステップにより、前記プログラムのコードを生成する、プログラムの生成方法。
【請求項２】
プログラムに含まれる、手続相互の呼び出し関係を作成する第１のステップと、
前記呼び出し関係を用いて、前記プログラムの手続に含まれる関数を、再帰呼び出しを行なう関数と、行なわない関数に分ける第２のステップと、
前記再帰呼び出しを行なわない関数に対して、その関数の自動変数を静的変数へ変換する第３のステップにより、前記プログラムのコードを生成する、プログラムの生成方法。
【請求項３】
請求項１又は請求項２記載のプログラムの生成方法において、前記呼び出し関係はコールグラフであるプログラムの生成方法。
【請求項４】
プログラムに含まれる手続相互の呼び出し関係から、当該プログラムの手続に含まれる関数であって再帰呼び出しを行なわないものを抽出し、当該関数の自動変数をグローバル変数へ変換するプログラム。
【請求項５】
プログラムに含まれる手続相互の呼び出し関係から、当該プログラムの手続に含まれる関数であって再帰呼び出しを行なわないものを抽出し、当該関数の自動変数を静的変数へ変換するプログラム。
【発明の詳細な説明】
【技術分野】
【０００１】
本発明は、不正を働くプログラムから計算機システムを防御する技術に係り、特に、バッファーオーバーフローを利用した不正コードの実行を防ぐ、プログラムの生成技術に関する。
【背景技術】
【０００２】
サーバプログラムには、C言語やC++言語で記述されたプログラムが多数あり、これらの言語では、配列の宣言範囲を超えないにようにプログラムを記述する必要がある。しかし、プログラムのバグにより、宣言範囲を超える場合がある。
悪意のある者、または、悪意のある者が作成したプログラム(以降、両方をまとめて悪意者とする)が、このようなプログラムに存在するバグを利用し、本来のプログラム動作とは異なる動作をさせ、不正に計算機システムを利用するという、セキュリティーの問題がある。かかる問題を解決するには、いくつか方法があるが、不正なコードの実行を検出し回避することと、検出時のオーバーヘッドを小さくすることが重要である。
不正な動作は、プログラム中の関数内にある自動変数の配列（バッファー）のオーバーフローを利用したものが多い。ここで、「オーバーフローを利用する」とは、配列変数の宣言における添え字の範囲を超えた部分への書き込みをいう。
【０００３】
通常、関数内にある自動変数は、スタックと呼ばれる領域に置かれる。スタックには、関数呼び出しのための、引数やスタックのフレームポインタ、関数の戻りアドレスその他所定の情報が置かれる。悪意者が、バッファーのオーバーフローにより範囲外の部分へ、何らかのコードその他不正な情報を書き込み、さらに、スタック上の関数の戻りアドレスを不正なコードが存在するアドレスへ書き換えた場合を考える。このとき、関数が呼び出し元へ戻る際に、この書き換えられた不正なコードのアドレスへ移動し、以後、不正なコードが実行され、悪意者の意図する不正動作が行われる。
【０００４】
図６にバッファーオーバーフローの具体例を示す。
プログラム７０１は、２行目で配列変数bufferを宣言している。この配列変数bufferは、関数subの実行時にスタック上に置かれる自動変数である。この関数subを呼び出したときのスタックの状態を、スタック７０２に示す。関数subを呼び出した手続きへ戻るためのアドレス（ret. addr.）、スタックのフレームポインタ（sfp）、自動変数の配列変数buffer[0-3]、… buffer[124-127] が配置されている。
プログラム７０１の３行目に従い、関数subが関数set_bufferを呼び出し、関数set_buffer内で配列変数bufferへの書き込みが行なわれるときに、配列変数bufferの添え字の値が128以上になった場合には、配列変数bufferが宣言した範囲以外の部分へアクセスすることができることとなる（バッファーオーバーフロー）。これを利用して悪意者は、本来のプログラムの関数の戻りアドレス、スタックのフレームポインタその他の情報を書き換えることができ、また、不正なコードを書き込むこともできるようになる。このようにして、不正なコードの実行が可能となる。このような問題を解決するために、以下の方法がある。
【０００５】
非特許文献１のStackGuardは、スタック上にある戻りアドレスが格納されている場所と、バッファーが格納されている場所の間に、書き換えを検出するための値を入れる、canaryと呼ばれる場所を設ける。このcanaryへ不規則な値を入れておく。バッファーのオーバーフローによる戻りアドレスの書き換えが起きると、canaryの値も書き換えられるため、関数が呼び出し元へ戻る直前に、この値が書き換えられていることをチェックすることで、オーバーフローを検出することができ、不正なコードへの移動を回避できる。
図７に非特許文献１を適用した例を示す。
プログラム７０１（図６）は、プログラム８０１のように変換される。プログラム８０１の２行目で、書き換え動作を検出するための値を入れる場所であるcanaryを宣言する。この環境下で、関数subが呼び出されたときのスタックの状態をスタック８０２に示す。スタック８０２に示すようにcanaryがスタックのフレームポインタ（sfp）と配列変数bufferの間に置かれる。
プログラム８０１の４行目で、関数subの本来の処理を行なう前にcanaryへ不規則な値を代入し、プログラム８０１の６行目で、関数subの本来の処理が終わった後にcanaryの値をチェックする。配列変数bufferのオーバーフローが起きると、canaryの値も書き換えられるので、canaryの値が４行目で代入した値と異なる値が入る。そのため、canaryの値をチェックすることでバッファーのオーバーフローを検出することができる。バッファーのオーバーフローを検出したら、プログラム８０１の７行目でオーバーフローが起きたときの処理を行なう。
【０００６】
非特許文献２の方法は、スタックを制御用とデータ用に分け、制御用のスタックに戻りアドレスやスタックのフレームポインタを置き、関数呼び出しの引数や関数内の自動変数はスタックに配置することで、プログラムの不正な動作を防ぐものである。これによりスタック上にあるバッファーの範囲外へデータを書き込んでも、関数の戻りアドレスが制御用のスタック上にあるために書き換えられず、不正なコードへの移動を回避できる。
【０００７】
非特許文献３のLibsafeは、範囲チェックを行なっていない危険なライブラリを、範囲チェックを行なう安全なライブラリへ置き換えることにより、オーバーフローを検出する方法である。これにより、不正なコードの書き込みや、戻りアドレスの書き換えを行なえないようにすることができる。
【０００８】
【非特許文献１】Crispin Cowan, Calton Pu, Dave Maier, Heather Hinton, ”StackGuard: Automatic Adaptive Detection and Prevention of Buffer-Overflow Attacks”, Proc. 7th USENIX Security Conference, (1998).
【非特許文献２】Jun Xu, Zbigniew Kalbarczyk, Sanjay Patel and Ravishankar K. Iyer, "Architecture Support for Defending Against Buffer Overflow Attacks", EASY-2 Workshop, (2002).
【非特許文献３】Timothy K. Tsai, Navjot Singh, "Libsafe: Protecting Critical Elements of Stacks", White Paper http://www.research.avayalabs.com/ project/libsafe/, (1999).
【発明の開示】
【発明が解決しようとする課題】
【０００９】
非特許文献１では、canaryへ値を代入し、また、canaryの値を調べ、オーバーフローが起きたか否かを検査する。このため検査の時間を費やすこととなるので（実行時のオーバーヘッド）、プログラムの実行速度が低下してしまう。
【００１０】
図９を用いて、バッファーを引数として関数呼び出しを行なった場合、呼び出し先で、そのバッファーのオーバーフローのチェックを行なっていないと生じる問題を説明する。もし、呼び出し先の関数内において引数で渡されたバッファーのオーバーフローが起き、呼び出し先の関数の戻りアドレスが書き換えられたとしても、呼び出し先の関数から呼び出し元関数へ戻る時にはオーバーフローの検査が行なわれずに、不正なアドレスへ移動することが可能になる。
図７のプログラム８０１において、関数set_bufferを呼び出した時のスタックの状態が図９のスタック１１００である。スタック８０２の状態と異なり、スタックに引数変数のbufferが置かれ、関数set_bufferが呼び出されて関数set_bufferの戻りアドレスが置かれ、関数set_bufferのスタックのフレームポインタが置かれ、関数set_bufferの自動変数が置かれる。この状態で、関数set_buffer内において、配列変数bufferの添え字が０以下で書き込みが起きた場合、バッファーオーバーフローにより、悪意者は、関数set_bufferの戻りアドレスを変更することができる。この後、関数set_bufferが終了し、関数subへ戻るときに、書き換えられた不正なアドレスへフレームポインタが移動する。関数set_bufferでcanaryが置かれていても、canaryの位置までオーバーフローが起きなければ、オーバーフローの検出ができない。
【００１１】
非特許文献２の方法は、ハードウェアの変更が必要であり、システム変更のためのコストが高いという問題がある。
【００１２】
非特許文献３の方法は、ライブラリを置き換えるだけなので、ライブラリ以外のプログラム部分にあるバッファーオーバーフローには対応できないという問題がある。
【課題を解決するための手段】
【００１３】
プログラム中で使用する変数が置かれるメモリ領域には、自動変数が置かれるスタック、初期値の有る変数が置かれるデータセクション、初期値がない変数が置かれるBSSセクションがある。自動変数はスタックに置かれるが、グローバル変数や静的変数は、データセクションやBSSセクションに置かれる。このことを利用し、自動変数のバッファーをグローバル変数か、あるいは、静的変数へ変換することにより、バッファーへの書き込みをスタックへ行なわないようにする。これにより、悪意者は、バッファーオーバーフローによる戻りアドレスやフレームポインタの書き換えが行なえなくなり、不正なコードの存在するアドレスへの移動が出来なくなる。
自動変数をグローバル変数や静的変数へ変換すると、再帰呼び出しができなくなるので、再帰呼び出しがない部分にのみ適用する。再帰呼び出しを行なう関数には、従来の方法、例えば非特許文献１の方法を利用する。
【発明の効果】
【００１４】
非特許文献１で行っているような実行時のバッファーオーバーフローの検出を行なわないので、実行時オーバーヘッドがない。また、常にスタックにバッファーが存在しないので、戻りアドレスは書き換えられず、安全に戻りアドレスへ移動できる。
非特許文献２のようなハードウェアの変更も必要ない。
非特許文献３で対応していないプログラム部分のオーバーフローに対応している。再帰呼び出しを行なう関数に自動変数のバッファーがある場合、実行時に再帰呼び出しを行なうとスタック領域を全て使ってしまい、プログラムの実行が止まる可能性が高い。通常、このような再帰呼び出しを行なう関数には、バッファーを使わないように記述する。そのため、本発明は、再帰呼び出しを行なわない部分のみに対して適用しても有効である。
【発明を実施するための最良の形態】
【００１５】
情報処理装置３００の構成を図２に示す。情報処理装置３００は、CPU３０１、入力装置３０２、出力装置３０３、主記憶装置３０４、外部記憶装置３０７を備えて構成される。CPU３０１は情報処理装置の全体の制御を司るもので、外部記憶装置３０７に記憶される本実施の形態に係る各種動作を行なうためのコードから構成されるコード変換プログラム３０８を実行することにより、本実施形態に係る情報処理装置３００としての各種機能を実現する。
主記憶装置３０４は、CPU３０１によりワークエリアとして用いられるほか、後述するコールグラフ３０５と中間コード３０６を記憶する。外部記憶装置３０７には、コード変換プログラム３０８、ソースコード３０９、オブジェクトコード３１０、実行コード３１１が記憶されている。
なお、コールグラフとは、プログラムに含まれる１の手続を点で表す場合に、ある手続（Ａ）が別の手続（Ｂ）を呼び出す関係があるとき、（Ａ）から（Ｂ）へ向けた矢印で、呼び出し関係を表したグラフをいう。例えば、手続（Ａ）が、手続（Ｂ）及び手続（Ｃ）を呼び出す場合には、（Ａ）を起点として、（Ｂ）と（Ｃ）へ矢印の終点が位置するよう、それぞれ、２つの矢印が示されることとなる。コールグラフで表すには、プログラムに含まれる手続間の呼び出し関係が決定することを要する。
入力装置３０２は、情報処理装置３００へのデータ入力等のために用いられる装置であり、ユーザインターフェースとして機能する。出力装置３０３は、情報を外部へ出力するための装置であり、ユーザインターフェースとして機能する。
ソースコード３０９は、C言語やC++言語で記述されたプログラムである。オブジェクトコード３１０は、コード変換プログラム３０８によりソースコード３０９から生成される、情報処理装置３００が実行可能なプログラムコードである。この場合、コード変換プログラム３０８は、コンパイラとして機能する。実行コード３１１は、複数のオブジェクトコード３１０からコード変換プログラム３０８により生成される、情報処理装置３００が実行可能なプログラムコードである。この場合、コード変換プログラム３０８は、リンカとして機能する。
【００１６】
図３に、コンパイル及びリンクの手順を示す。まずコンパイラ４０１（つまりコード変換プログラム３０８）にソースコード３０９が入力されるとオブジェクトコード３１０が生成される。そしてリンカ４０２（つまりコード変換プログラム３０８）にオブジェクトコード３１０が入力されると実行コード３１１が生成される。
オブジェクトコード３１０には、コールグラフ作成のために、ソースコード３０９から生成される中間コード３０６（図２）が含まれている。また、そのためにコンパイラ４０１は、中間コード３０６を含んだオブジェクトコード３１０を生成する。コンパイラ４０１は、複数のソースコード３０９を入力することができるが、makeコマンドなどのツールを利用する場合は、１つのソースコード３０９を入力し、１つのオブジェクトコード３１０を生成する。
リンカ４０２は、プログラムコードを構成するすべてのオブジェクトコード３１０を入力とするため、リンカ４０２では、プログラムコードを構成する全ての関数がそろう。そのため、プログラムコードを構成する全ての関数の呼び出し関係をコールグラフ３０５（図２）として作成することができる。
【００１７】
図４に、コンパイラ４０１の構成図を示す。
ますコンパイラ４０１は、ソースコード３０９を入力とし、中間コード生成５０１により中間コード３０６を生成する。制御フロー解析５０２では、中間コード３０６を入力とし制御フローを解析する。データフロー解析５０３では、中間コード３０６を入力としデータフローを解析する。最適化５０４では、制御フロー解析５０２の解析結果とデータフロー解析５０３の解析結果を用いて中間コード３０６を合理化し、情報処理装置１００で実行可能なコードへ変換する。コンパイラ４０１は最適化５０４で変換したコードと中間コード３０６を含んだオブジェクトコード３１０を出力する。
【００１８】
図５に、リンカ４０２の構成図を示す。
まずリンカ４０２は、オブジェクトコード３１０を入力とし、中間コード読取６０１において中間コード３０６を取り出す。次に、自動変数変換６０２において、本発明による自動変数の変換を行なう。その後、制御フロー解析５０２、データフロー解析５０３、最適化５０４を行い、実行コード３１１を出力する。
【実施例１】
【００１９】
自動変数をグローバル変数へ変換する方法について述べる。自動変数変換６０２のアルゴリズムを図１に示す。
【００２０】
まず、START１０１から始まり、ステップ１０２において中間コード３０６を入力としコールグラフ３０５（図２）を作成する。コールグラフの詳細については、文献「D. C. Atkinson. " Call graph extraction in the presence of function pointers", In Proc. 2002 Int. Conf. On Softw. Eng. Res. Prac., Page 579-584, (2002).」が参考となる。
ステップ１０３（図１）では、コールグラフ３０５をたどりループを検出する。つまり、コールグラフ３０５のデータの中に、手続（Ａ）→手続（Ｂ）、手続（Ｂ）→手続（Ｃ）、手続（Ｃ）→手続（Ａ）の関係があれば、手続（Ａ）（Ｂ）（Ｃ）はループを構成している。ループの検出方法の詳細については、文献「M. Wolfe, "High Performance Compilers for parallel computing", Addison-Wesley Publishing Company, Page 60-64, (1996).」が参照となる。ステップ１０４では、ステップ１０３で検出したループに含まれる関数に対して、再帰の印をつける。ステップ１０５では、コールグラフ３０５に含まれる全ての手続きに対して、ステップ１０７以降の処理を適用するようにする。ここでは、１０７からの一連の処理を行なう対象の関数をｆとする。
ステップ１０７では、現在処理中の関数ｆに再帰の印がついているか否かを調べる。再帰の印がついていればステップ１０８へ進み、なければステップ１０９へ進む。ステップ１０８では、関数ｆに対して従来のセキュリティー対策を適用する。例えば非特許文献１の方法を適用する。その後、ステップ１０５へ戻る。ステップ１１０では、関数ｆにある配列の自動変数をグローバル変数へ変更する。ステップ１１１では、ステップ１１０で変換したグローバル変数の名前を一意になるように名前を変更し、ステップ１０５へ戻る。ステップ１０５で処理する手続きがなくなればEND１０６へ進む。
【００２１】
図１のアルゴリズムを図６のプログラム７０１に適用した例を図８に示す。
関数subが再帰呼び出し関数でないと仮定すると、図１のアルゴリズムでは、ステップ１０２のコールグラフ作成後、ステップ１０３でループを検出し、ステップ１０４では関数subは再帰の印がつかない。ステップ１０５で全ての関数に対してステップ１０７以降の処理が繰り返され、ステップ１０７で関数subが対象になった場合は、再帰の印がついていないので、ステップ１１０へ進む。ステップ１１０では、図６のプログラム７０１の２行目の配列変数bufferを、図８の１行目のようにグローバル変数へ変換する。ステップ１１１では、ステップ１１０で変換した配列変数bufferの名前をsub_bufferと変更する。
かかる名前の変更は、同じ名前のグローバル変数で、異なる意味に用いられる変数が、同じ記憶領域を使うようになる問題を避けるために行なう。互いに異なる関数内では、同じ名前の自動変数がそれぞれ異なった意味に使え、それぞれ異なる領域に置かれる。そして、これらの自動変数が共にグローバル変数へ変更された場合、同じ名前のグローバル変数にならぬようにするため、名前の変更をするのである。
配列変数bufferをグローバル変数へ変更すると、変数に初期値がないためBSSセクション９０３に置かれ、スタックの状態はスタック７０２からスタック９０２のようになる。これにより、悪意者により、配列変数bufferにオーバーフローが起きても、戻りアドレスの書き換えが出来なくなる。
【実施例２】
【００２２】
自動変数を静的変数へ変換する方法について述べる。自動変数変換６０２のアルゴリズムは、図１のステップ１１０及びステップ１１１の部分に代えて、ステップ２１０（配列の自動変数を静的変数へ変更）としたものである。残りは図１のステップと同様であり説明を省略する。
ステップ２１０では、関数ｆの配列の自動変数を静的変数へ変更し、ステップ１０５へ戻る。
自動変数変換６０２のアルゴリズムを図６のプログラム７０１に適用することを考える。実施例１と同じように、関数subが再帰呼び出し関数でないと仮定すると、ステップ１０７までは実施例１と同様である。ステップ２１０で、図６のプログラム７０１の２行目の配列変数bufferを、「static char buffer[128];」のように静的変数へ変換する。配列変数bufferを静的変数へ変更すると、変数に初期値がないためBSSセクションに置かれ、スタックの状態は図８のスタック９０２と同様になる。これにより、配列変数bufferにオーバーフローが起きても、戻りアドレスの書き換えが起きなくなる。
【産業上の利用可能性】
【００２３】
実行時のオーバーヘッドを費やすことなく、プログラムにおけるスタック上のデータ破壊を回避し、悪意者による不正なコードの実行を阻止して、セキュリティーホールのない計算機システムを構築することに貢献できる。
【図面の簡単な説明】
【００２４】
【図１】自動変数をグローバル変数へ変換する自動変数変換６０２のアルゴリズムである。
【図２】情報処理装置３００の構成図である。
【図３】コンパイルおよびリンクの手順を示す図である。
【図４】コンパイラ４０１の構成図である。
【図５】リンカ４０２の構成図である。
【図６】バッファーオーバーフローの具体例を説明するための、プログラムとそのスタックの状態を示す図である。
【図７】非特許文献１のStackGuardを適用したときの、プログラムとそのスタックの状態を示す図である。
【図８】図１のアルゴリズムを図６のプログラム７０１に適用した結果を示す図である。
【図９】バッファーを引数として関数呼び出しを行なったときの問題点を説明するための図である。
【符号の説明】
【００２５】
１０８……従来のセキュリティー対策を実施
１１０……自動変数をグローバル変数へ変更
１１１……変数名を一意になるように変更
２１０……自動変数を静的変数へ変更
【図１】
2007004647.tif 000003
【図２】
2007004647.tif 000004
【図３】
2007004647.tif 000005
【図４】
2007004647.tif 000006
【図５】
2007004647.tif 000007
【図６】
2007004647.tif 000008
【図７】
2007004647.tif 000009
【図８】
2007004647.tif 000010
【図９】
2007004647.tif 000011