(57)【要約】【課題】  関数ポインタ変数に対するバッファオーバフロー攻撃を防御可能なプログラムまたはオブジェクトコードを生成することを目的とする。また、セキュリティチェック処理による実行時オーバヘッドを軽減させることを目的とする。【解決手段】  入力プログラム中の関数ポインタ変数を検出し、当該関数ポインタが指すことが可能な関数のリストを作成し、当該関数ポインタ変数を用いた関数呼出しの直前に、当該関数リストを使用して、当該関数ポインタ変数の値が不正な値でないか調べる処理を追加する。【選択図】      図１
2007041777.tif 000002
【特許請求の範囲】
【請求項１】
ソースプログラムを入力して、コンピュータで実行可能なプログラムまたはオブジェクトコードを生成するコンピュータにより実行される言語処理方法であって、
前記コンピュータは、
前記ソースプログラムを格納する記憶部と、処理部とを備え、
前記処理部は、
前記記憶部から前記ソースプログラムを読み出し、そのソースプログラムの構文を解析して、当該ソースプログラムを中間語に変換するステップと、
当該中間語中の関数ポインタ変数により呼出される可能性がある関数の関数リストを作成するステップと、
当該関数ポインタ変数が当該関数リストに登録されている関数を指すか否かを判定し、当該関数ポインタ変数が当該関数リストに登録されていない関数を指すと判定された場合には、関数呼出しを中止する関数ポインタチェックコードを、前記中間語中の関数ポインタ変数を用いた関数呼出しの直前に挿入するステップと、
前記関数ポインタチェックコードが挿入された当該中間語を、前記コンピュータで実行可能なプログラムまたはオブジェクトコードに変換するステップと
を実行することを特徴とする言語処理方法。
【請求項２】
前記処理部は、前記関数のリストを作成するステップにおいて、
前記中間語を用いて、依存解析処理によって前記関数ポインタ変数を用いた関数呼出しへ到達する代入文を見つけ、当該代入文の右辺変数を前記関数リストに加える
ことを特徴とする請求項１に記載の言語処理方法。
【請求項３】
前記記憶部は、あらかじめ設定された特定の関数をさらに格納し、
前記処理部は、前記関数ポインタチェックコードを挿入するステップにおいて、
前記関数ポインタ変数が前記特定の関数を指すか否かを判定し、当該関数ポインタ変数が当該特定の関数を指すと判定した場合に、前記関数ポインタチェックコードを挿入する
ことを特徴とする請求項１または２に記載の言語処理方法。
【発明の詳細な説明】
【技術分野】
【０００１】
本発明は、ソースプログラムを入力して、コンピュータで実行可能なプログラムまたはオブジェクトコードを生成する言語処理方法に関する。
【背景技術】
【０００２】
プログラムに対するセキュリティ攻撃の１つに、バッファオーバフロー攻撃がある。これは、プログラムの関数スタック上のローカル変数に変数サイズ以上の長さの不正な入力データを与えることによって、関数スタックの破壊や、関数スタック上に格納されているリターンアドレスの書き換えなどを行い、プログラムの制御を奪うものである。
【０００３】
従来、このような攻撃を防御するコンパイラを用いた言語処理方法として、StackGuardが知られている（例えば、非特許文献１参照）。これは、関数プロローグで、関数スタックのリターンアドレス格納部の直前にcanaryと呼ばれる特別な値を設定し、関数エピローグでcanaryの値が変更されていたら、バッファオーバフロー攻撃を受けたと判断してプログラムを終了させるという方法である。ここで、関数プロローグとは、コンパイラが関数内に追加する前処理のことであり、関数エピローグとは、コンパイラが関数内に追加する後処理のことである。
【０００４】
また、別の言語処理方法としてPointGuardが知られている（例えば、非特許文献２参照）。これは、関数ポインタの値をメモリ上では暗号化しておき、関数ポインタがプロセッサのレジスタにロードされる場合に、関数ポインタの値を復号化するという方法である。PointGuardの方法を適用すると、バッファオーバフロー攻撃を受けて関数ポインタの値を書き換えられても、プロセッサのレジスタにロードされる値は書き換えた値を更に復号化した値になるため、攻撃を行った悪意あるユーザが書き換えた値はロードされず、攻撃は成功しない。
【非特許文献１】Crispin Cowan, Calton Pu, Dave Maier, Jonathan Walpole, Peat Bakke, Steve Beattie, Aaron Grier, Perry Wagle, and Qian Zhang “StackGuard: Automatic Adaptive Detection and Prevention of Buffer-Overflow Attacks”, Proceedings of the 7th USENIX Security Symposium, pp.26-29, 1998
【非特許文献２】Crispin Cowan, Steve Beattie, John Johansen, and Perry Wagle, “PointGuard(TM): Protecting Pointers From Buffer Overflow Vulnerabilities”, Proceedings of the 12th USENIX Security Symposium, 2003
【発明の開示】
【発明が解決しようとする課題】
【０００５】
非特許文献１のStackGuardは、関数スタック上のローカル変数から関数スタック上のリターンアドレスまでを、不正な入力データが書きつぶすことを前提とした防御方法である。そのため、このような書きつぶしを行わない攻撃を防御することはできない。
【０００６】
例として、関数ポインタ変数が、関数スタック上の変数、つまりローカル変数である場合を考える。当該関数ポインタ変数以外のローカル変数に、関数ポインタ変数を書き換えることはできるがリターンアドレスを書き換えるほどは長くない不正な入力データが与えられるとする。この場合、リターンアドレスは書き換えられないので、前記従来技術のStackGuardでは攻撃を検出できない。また、不正な入力データが与えられた点からこの関数が終了するまでの間に、書き換えられた関数ポインタ変数を用いた関数呼出しがあった場合、書き換えられた値をアドレスとしてもつ場所へ制御が移ってしまう。このアドレスが悪意あるコードの先頭であった場合は、攻撃を仕掛けたユーザに制御が奪われる可能性がある。
【０００７】
非特許文献２のPointGuardは、関数ポインタ変数への書き込み時およびレジスタへの読み出し時に、それぞれ値の暗号化、復号化を行うので、関数ポインタ変数がローカル変数であった場合でも攻撃を検出できる。しかし、関数ポインタ変数へのすべての書き込み／読み出し時に値の暗号化／復号化を行うので、このセキュリティチェック処理による実行時オーバヘッドが大きいという問題があった。
【０００８】
本発明は、以上のような問題点に鑑みてなされたものであり、関数ポインタ変数に対するバッファオーバフロー攻撃を防御可能なプログラムまたはオブジェクトコードを生成することを目的とする。また、セキュリティチェック処理による実行時オーバヘッドを軽減させることを目的とする。
【課題を解決するための手段】
【０００９】
前記目的を達成するため、本発明の言語処理方法は、ソースプログラムを入力して、コンピュータで実行可能なプログラムまたはオブジェクトコードを生成するコンピュータにより実行される言語処理方法であって、前記コンピュータが、前記ソースプログラムを格納する記憶部と、処理部とを備え、前記処理部が、前記記憶部から前記ソースプログラムを読み出し、そのソースプログラムの構文を解析して、当該ソースプログラムを中間語に変換するステップと、当該中間語中の関数ポインタ変数により呼出される可能性がある関数の関数リストを作成するステップと、当該関数ポインタ変数が当該関数リストに登録されている関数を指すか否かを判定し、当該関数ポインタ変数が当該関数リストに登録されていない関数を指すと判定された場合には、関数呼出しを中止する関数ポインタチェックコードを、前記中間語中の関数ポインタ変数を用いた関数呼出しの直前に挿入するステップと、前記関数ポインタチェックコードが挿入された当該中間語を、前記コンピュータで実行可能なプログラムまたはオブジェクトコードに変換するステップと、を実行することを特徴とした。
【００１０】
かかる方法によれば、関数ポインタ変数に対するバッファオーバフロー攻撃を防御可能なプログラムまたはオブジェクトコードを生成することが可能である。また、セキュリティチェックによる実行時オーバヘッドを軽減させることができる。
【００１１】
ここで、関数呼出しの直前とは、関数ポインタ変数による関数の呼出し実行直前を意味する。例えば、行や、＜＞、カンマ等で区切られた列の直前が該当する。しかし、たとえ関数の呼出し実行前であっても、関数ポインタ変数に設定されているアドレスの値を書き換える処理がある場合には、その処理よりも前であってはならない。関数ポインタチェックコードによってチェックされたアドレスと、実際にその関数ポインタを用いて呼出す関数のアドレスとが異なってしまう可能性があるからである。
【発明の効果】
【００１２】
本発明の言語処理方法によれば、関数ポインタ変数に対するバッファオーバフロー攻撃を防御可能なプログラムまたはオブジェクトコードを生成することが可能である。また、セキュリティチェックによる実行時オーバヘッドを軽減させることが可能である。
【発明を実施するための最良の形態】
【００１３】
（第１の実施形態）
以下、図面を用いて本発明の第１の実施形態について説明する。
図１は、第１の実施形態における高信頼プログラム生成コンパイラの機能例を示す図である。
図１に示すように、高信頼プログラム生成コンパイラ１０は、構文解析部１１と、高信頼プログラム生成部１３と、コード生成部１５とを含んで構成されている。
【００１４】
構文解析部１１は、入力プログラム９０を入力して中間語９１を出力する。構文解析部１１の処理は、通常のコンパイラが有する公知の構文解析の機能による処理と特に変わらないため、重複説明は省略する。
【００１５】
高信頼プログラム生成部１３は、中間語９１を入力し、後記するチェックコード等を挿入した中間語９１を出力する。高信頼プログラム生成部１３は、関数リスト作成部１３１と、関数ポインタチェック挿入部１３３とを含んで構成されている。
関数リスト作成部１３１は、中間語９１を入力して、その中からローカルな関数ポインタを見つけ、当該関数ポインタが指す可能性のある関数のリストである関数リスト９３を出力する。
関数ポインタチェック挿入部１３３は、中間語９１および関数リスト９３を入力して、当該関数ポインタ変数を用いた関数呼出しの直前に、当該関数ポインタ変数の値が不正な値でないかを調べる関数ポインタチェックコードを挿入し、中間語９１を出力する。関数ポインタチェック挿入部１３３は、関数ポインタ変数の値が、関数リスト９３に登録されているものでなければ、不正な値であると判定する。
【００１６】
コード生成部１５は、関数ポインタチェックコードを挿入した中間語９１を入力して、プログラムまたはオブジェクトコードである出力プログラム９２を出力する。コード生成部１５の処理は、通常のコンパイラが有する公知のコード生成の機能による処理と特に変わらないため、重複説明は省略する。
【００１７】
図２は、第１の実施形態における高信頼プログラム生成コンパイラ１０（図１参照）による処理を実行する言語処理装置のハードウェア構成の一例を示す図である。
図２において、言語処理装置５２０は、コンピュータであり、表示装置５２１と、入力装置５２２と、外部記憶装置５２３と、情報処理装置５２４と、記録媒体５２５と、駆動装置５２６とを含んで構成される。表示装置５２１は、ＬＣＤ（Liquid Crystal Display）等によって構成され、文字や画像等を表示する。入力装置５２２は、キーボードやマウス等によって構成され、操作者からの指示を入力する。外部記憶装置５２３は、ＨＤＤ（Hard Disk Drive）等によって構成され、大容量のデータやプログラム等を記憶する。情報処理装置５２４は、処理部５２４１と記憶部５２４２とを含んで構成され、処理部５２４１は、例えばＣＰＵ（Central Processing Unit）等の処理装置であり、記憶部５２４２は、例えば主メモリ等の記憶装置である。情報処理装置５２４は、蓄積プログラム方式による演算処理を行う。記録媒体５２５は、後記処理手順に係るプログラムやデータを記録した光ディスク等である。駆動装置５２６は、記録媒体５２５を駆動する装置である。
【００１８】
情報処理装置５２４は、駆動装置５２６を介して読み出された記録媒体５２５内のデータやプログラム等を外部記憶装置５２３に記憶させる。情報処理装置５２４は、外部記憶装置５２３に記憶されたデータやプログラムを主メモリにロードし、そのデータを利用し、プログラムを実行する。これにより、各処理部によって構成される高信頼プログラム生成コンパイラ１０（図１参照）や、高信頼プログラム生成リンカ２０（図１２参照）などといった高信頼プログラムを生成するためのソフトウェアを実現する。
【００１９】
図３は、第１の実施形態における入力プログラム９０（図１参照）の一例を示す図である。ここでは、入力プログラム９０１は、コンパイルに必要なすべてのソースプログラムであると仮定する。入力プログラム９０１の左端にある番号は行番号を示している。
入力プログラム９０１の１行目は標準ライブラリヘッダファイルstdlib.hをインクルードする文である。２行目は関数funcのプロトタイプ宣言である。３〜４行目は入力プログラム９０１の主処理関数mainであり、その中の４行目はプログラム起動時のコマンドライン第１引数を実引数として関数funcを呼び出す関数呼出し文である。５〜１１行目は関数funcの本体である。６行目でローカルな関数ポインタ変数testが宣言され、８行目でこの変数testに標準ライブラリ関数exitの関数ポインタを代入し、９行目で関数funcの第1引数inputの値を要素数３のローカル配列bufにコピーし、１０行目で関数ポインタtestを介して関数exitを呼び出す。
【００２０】
図４は、第１の実施形態における関数リスト９３（図１参照）の一例を示す図である。関数リスト９３１の第１フィールドには、次の関数リスト９３２へのポインタが設定され、関数リスト９３１の第２フィールドには、図３の入力プログラム９０１から呼び出される可能性のある関数funcの名前が登録されている。同様に、関数リスト９３２の第１フィールドには、次の関数リスト９３３へのポインタが設定され、関数リスト９３２の第２フィールドには、図３の入力プログラム９０１から呼び出される可能性のある関数strcpyの名前が登録されている。関数リスト９３３の第１フィールドには、次のリストがないことを表すNULL値が設定され、関数リスト９３３の第２フィールドには、図３の入力プログラム９０１から呼び出される可能性のある関数exitの名前が登録されている。図３の入力プログラム９０１に含まれる関数mainは、プログラム冒頭で1度だけ呼び出されるものであるので、関数リスト９３には登録しない。なお、関数リスト９３の表現については、線形リストに限らず、配列、ビットベクトル等による表現でも良い。
【００２１】
図５は、第１の実施形態における出力プログラム９２（図１参照）の一例を示す図である。ここでは、図３の入力プログラム９０１を図１における入力プログラム９０とした場合に得られる出力プログラム９２を示す。出力プログラム９２１の左端にある番号は行番号である。出力プログラム９２１の１〜９行目および１１〜１２行目は、入力プログラム９０１（図３参照）と同一なのでここでは説明しない。１０行目は、関数ポインタチェック挿入部１３３（図１参照）が挿入した行であり、１１行目の関数ポインタ変数testによる関数呼出し文の直前に、関数ポインタ変数の値が不正な値でないかを調べる関数＿is＿legal＿func＿ptrを呼出し、その返り値を引数として標準ライブラリ関数assertを呼び出す処理である。１３〜１８行目は関数ポインタチェック挿入部１３３（図１参照）が挿入した部分であり、引数で与えられた関数ポインタ変数の値が不正な値でないかを調べる関数＿is＿legal＿func＿ptrの本体である。１４〜１７行目は、引数で与えられた関数ポインタの値が、プログラム内で呼び出される可能性のある関数であるfunc、strcpyまたはexitへの関数ポインタと等しいか否かを調べ、等しい場合は１を、どの関数ポインタとも一致しない場合は０を返す処理である。標準ライブラリ関数assertは、引数として関数＿is＿legal＿func＿ptrの返り値１を受け取った場合はなにもせず、返り値０を受け取った場合は、標準エラー出力にエラーメッセージを出力してからプログラムを終了させる。プログラム内で呼び出される可能性のある関数は、図４の関数リスト９３１〜９３３を使用して列挙する。
【００２２】
図６は、第１の実施形態における関数リスト作成部１３１（図１参照）の処理手順を表すフローチャートである。以下では、関数リスト作成部１３１（図１参照）の処理を中間語９１（図１参照）に適用する場合について説明するが、ここでの中間語は、図３の入力プログラム９０１と同等のものであるため、適宜、入力プログラム９０１を用いて説明する。
【００２３】
まず、プログラム中の関数プロトタイプ宣言のうち、未処理のものを１つ選ぶ（Ｓ１０１）。プログラム中にinclude文がある場合は、プリプロセッサによってinclude文がすべて展開された後で本処理を行う。ここでは、未処理の関数プロトタイプ宣言の例として、入力プログラム９０１（図３参照）の２行目にある関数funcのプロトタイプ宣言を選択する。次に、選択した関数名を関数リストに登録する（Ｓ１０２）。ここでは、選択した関数funcのプロトタイプ宣言から関数名funcを取り出し、図４の関数リスト９３１の第２フィールドに書き込む。次に、プログラム中の全関数プロトタイプ宣言を処理したか否かを判定する（Ｓ１０３）。全関数プロトタイプ宣言を処理していない場合（Ｓ１０３でＮｏ）は、Ｓ１０１に戻って処理を継続し、全関数プロトタイプ宣言を処理した場合（Ｓ１０３でＹｅｓ）は、処理を終了する。ここでは、プロトタイプ宣言から関数名func、strcpyおよびexitをすべて取り出すまではＳ１０１に戻って処理を継続し、すべて取り出した後は、処理を終了する。
【００２４】
図７は、第１の実施形態における関数ポインタチェック挿入部１３３（図１参照）の処理手順を表すフローチャートである。ここで例として扱う中間語は、入力される中間語は図３の入力プログラム９０１と、出力される中間語は図５の出力プログラム９２１と同等なので、適宜、入力プログラム９０１（図３参照）または出力プログラム９２１（図５参照）を用いて説明する。
【００２５】
まず、プログラム中の関数のうち、未処理のものを１つ選ぶ（Ｓ２０１）。プログラム中にinclude文がある場合は、プリプロセッサによってinclude文がすべて展開された後で本処理を行う。ここでは、未処理の関数の例として、入力プログラム９０１（図３参照）の５〜１１行目にある関数funcを選択する。次に、選択した関数のローカル変数宣言に未処理の関数ポインタ変数宣言があるか否かを判定する（Ｓ２０２）。未処理の関数ポインタ変数宣言がある場合（Ｓ２０２でＹｅｓ）は、Ｓ２０３に進む。未処理の関数ポインタ変数宣言がない場合（Ｓ２０２でＮｏ）は、Ｓ２０４に進む。ここでは、入力プログラム９０１（図３参照）の６行目に示すように、選択した関数funcのローカル変数宣言の中に未処理の関数ポインタ変数宣言testがあるので、Ｓ２０３に進む。
【００２６】
Ｓ２０３では、Ｓ２０１において選択された関数内の関数ポインタ変数を用いたすべての関数呼出しに対して、その直前に関数ポインタチェック関数呼出しを挿入し（Ｓ２０３）、Ｓ２０２に戻る。Ｓ２０２の詳細は前記したのでここでは述べない。ここでは、中間語９１（図１参照）の中から、Ｓ２０２で見つけた関数ポインタ変数testを用いた関数呼出し文をすべて探し、当該関数呼出し文の直前に、当該関数ポインタ変数を引数とした関数ポインタチェック関数の呼出し文を挿入する。出力プログラム９２１では、１０行目の関数呼出し文が挿入された文に相当する。そして、関数ポインタ宣言がtest以外にはないので、Ｓ２０４に進む。
【００２７】
Ｓ２０４では、プログラム中の全関数を処理したかを判定する（Ｓ２０４）。プログラム中の全関数が処理済であればＳ２０５に進み、そうでなければＳ２０１に戻って処理を継続する。ここでは、中間語９１（図１参照）内に、関数がfunc以外にないので、全関数が処理済であり、Ｓ２０５に進む。
【００２８】
Ｓ２０５では、関数リスト９３（図１参照）を用いて、関数ポインタチェック関数の本体を作成し、作成した本体をプログラム中に挿入する（Ｓ２０５）。ここでは、関数リスト９３１〜９３３（図４参照）を順にたどって関数リスト第２フィールドの関数名を抜き出し、抜き出した関数名で表される関数の関数ポインタと関数ポインタチェック関数の引数とを比較し、一致すれば１を、どの関数とも一致しなければ０を返す関数を作成する。出力プログラム９２１（図５参照）では、１３〜１８行目の処理が、本ステップで作成する関数に相当する。
【００２９】
このように、本実施形態に示した言語処理方法は、ソースプログラムを入力して、コンピュータで実行可能なプログラムまたはオブジェクトコードを生成するコンピュータにより実行される方法であり、ソースプログラムを格納する記憶部と処理部を備えたコンピュータの処理部が、記憶部からソースプログラムを読み出し、そのソースプログラムの構文を解析して、ソースプログラムを中間語に変換するステップと、中間語中の関数ポインタ変数により呼出される可能性がある関数の関数リストを作成するステップと、関数ポインタ変数が関数リストに登録されている関数を指すか否かを判定し、関数ポインタ変数が関数リストに登録されていない関数を指すと判定された場合には、関数呼出しを中止する関数ポインタチェックコードを、中間語中の関数ポインタ変数を用いた関数呼出しの直前に挿入するステップと、関数ポインタチェックコードが挿入された中間語を、コンピュータで実行可能なプログラムまたはオブジェクトコードに変換するステップとを実行する方法である。
【００３０】
本実施形態で示したように、例えば、関数リスト作成部１３１（図１参照）は、プログラム中の関数プロトタイプ宣言に存在する関数名を関数リスト９３（図１参照）に登録することが可能である。続いて、関数ポインタチェック挿入部１３３（図１参照）は、プログラム中の関数内のローカル変数宣言を検出し、その関数ポインタ変数を用いたすべての関数呼出しに対して、その直前に関数ポインタチェック関数呼出しを挿入することが可能である。その後、関数ポインタチェック挿入部１３３（図１参照）は、関数リスト９３（図１参照）を用いて、関数ポインタチェック関数の本体を作成し、作成した本体をプログラム中に挿入することが可能である。
【００３１】
以上のように、本実施形態によれば、関数ポインタ変数に対するバッファオーバフロー攻撃を防御可能なプログラムまたはオブジェクトコードを生成することが可能である。また、セキュリティチェックによる実行時オーバヘッドを軽減させることが可能である。
【００３２】
（第２の実施形態）
次に、本発明の第２の実施形態について説明する。この第２の実施形態は、関数リスト作成部１３１（図１参照）に関するものであり、第1の実施形態と共通する部分については、重複説明を省略する。
第２の実施形態では、図８に示す入力プログラム９５１、図９に示す関数リスト９３４を例にあげて説明する。この関数リスト９３４は、第１の実施形態における関数リスト９３（図１参照）と異なり、代入文と呼出し点を組にして登録できるよう修正してある。具体的には、関数exitへのポインタが変数testに設定されている。変数testは、入力プログラム９５１（図８参照）の１２行目の関数呼出し文において関数呼出しに使われていることを示す情報である。
【００３３】
図１０は、第２の実施形態における出力プログラム９２（図１参照）の一例を示す図である。ここでは、図８の入力プログラム９５１を図１における入力プログラム９０とした場合に得られる出力プログラム９２を示している。以下では、図８の入力プログラム９５１との違いのみを説明する。出力プログラム９５２の１２行目は、図１の関数ポインタチェック挿入部１３３が挿入した行であり、１３行目の関数ポインタ変数testによる関数呼出し文の直前に、関数ポインタ変数testの値が不正な値でないかを調べる関数＿is＿legal＿func＿ptrを呼出し、その返り値を引数として標準ライブラリ関数assertを呼び出す処理である。１５〜１８行目は関数ポインタチェック挿入部１３３（図１参照）が挿入した部分であり、引数で与えられた関数ポインタ変数の値が不正な値でないかを調べる関数＿is＿legal＿func＿ptrの本体である。１６行目は、引数で与えられた関数ポインタの値が、１３行目の関数ポインタ変数testの呼出しに到達する定義値であるexitへの関数ポインタと等しいか否かを調べ、等しい場合は１を、そうでない場合は０を返す処理である。本実施形態では、関数＿is＿legal＿func＿ptrは、生成した関数リストを用いて、チェックする関数呼出し点毎に、関数名を変えて、当該関数呼出し点専用の処理として生成される。これにより、関数＿is＿legal＿func＿ptr内の不要なチェックコードが削減され、チェック処理を高速化することが可能である。
【００３４】
図１１は、第２の実施形態における関数リスト作成部１３１（図１参照）の処理手順を表すフローチャートである。以下では、関数リスト作成部１３１（図１参照）の処理を中間語９１（図１参照）に適用する場合について説明するが、適宜、図８の入力プログラム９５１を、本処理における入力プログラムとして用いる。
【００３５】
まず、プログラム中にある本体を持つ関数のうち、未処理のものを１つ選ぶ（Ｓ３０１）。ここでは、未処理の関数の例として、入力プログラム９５１（図８参照）の６〜１３行目にある関数funcを選択する。次にＳ３０２に進む。
【００３６】
Ｓ３０２では、選択した関数のローカル変数宣言に、未処理の関数ポインタ変数宣言が存在するかを調べ（Ｓ３０２）、存在すれば（Ｓ３０２でＹｅｓ）、Ｓ３０３へ進み、存在しなければ（Ｓ３０２でＮｏ）、Ｓ３０５へ進む。Ｓ３０５の処理については後記する。ここでは、未処理の関数ポインタ変数宣言として、入力プログラム９５１（図８参照）の７行目の変数testの宣言が選択され、Ｓ３０３に進む。
【００３７】
Ｓ３０３では、Ｓ３０２で選択した宣言をもつ関数ポインタ変数に未処理の呼出しがあるかを判定する（Ｓ３０３）。未処理の関数呼出しがあれば（Ｓ３０３でＹｅｓ）、Ｓ３０４へ進み、未処理の関数呼出しがなければ（Ｓ３０３でＮｏ）、Ｓ３０２に戻る。ここでは、未処理の関数呼出しとして、入力プログラム９５１の１２行目の変数testを用いた関数呼出し文が見つかり、Ｓ３０４に進む。
【００３８】
次に、Ｓ３０４では、関数リスト追加処理を行う（Ｓ３０４）。Ｓ３０４では、Ｓ３０２で発見された変数に関して依存解析を行い、Ｓ３０３で見つかった関数呼出し文における当該変数の値を設定する代入文を探し、もしその代入文における定義が当該関数呼出し文に到達するのであれば、そのような代入文の場所すべてを、呼出し点と組にして関数リストに登録し、Ｓ３０３に戻る。ここでは、Ｓ３０２で発見された変数testに関して依存解析を行い、当該変数の値を設定する代入文として、入力プログラム９５１の９行目、１０行目の代入文を検出する。このうち、１２行目に到達する定義は、１０行目の代入文であるので、この情報を図９に示した関数リスト９３４に登録する。ここでは、関数exitへのポインタが変数testに設定され、変数testは入力プログラム９５１の１２行目の関数呼出し文において関数呼出しに使われているという情報を設定している。なお、前記した依存解析は、例えば中田育男「コンパイラの構造と最適化」（朝倉書店、1999年）11〜12章に記載されている公知の方法と変わらない。例えば、メモリへの値の書き込みを定義、メモリからの値の読み込みを使用と呼ぶことにすると、プログラム中のある変数ｘに関する依存解析とは、変数ｘがどこで定義され、どこで使用されるかを調べることである。依存関係を調べることにより、例えば、プログラム中のある点で使用される変数の値が、どの点で定義された値であるかを知ることができる。なお、関数リスト９３４の表現については、線形リストに限らず、配列、ビットベクトルなどによる表現でも良い。
【００３９】
Ｓ３０５では、プログラム中の全関数を処理したか否かを判定する（Ｓ３０５）。未処理の関数があれば（Ｓ３０５でＮｏ）、Ｓ３０１に戻り、処理を継続する。また、未処理の関数なければ（Ｓ３０５でＹｅｓ）、関数リスト作成部１３１（図１参照）の処理を終了する。ここでは、関数名funcを取り出した後、処理を終了する。
【００４０】
このように、本実施形態に示した言語処理方法は、第１の実施形態に示した言語処理方法における関数のリストを作成するステップにおいて、処理部が、中間語を用いて、依存解析処理によって関数ポインタ変数を用いた関数呼出しへ到達する代入文を見つけ、代入文の右辺変数を関数リストに加える方法である。
【００４１】
本実施形態で示したように、例えば、関数リスト作成部１３１（図１参照）は、プログラム中にある本体を持つ関数のローカル変数宣言に関数ポインタ変数宣言が存在するかを調べ、発見された変数に関して依存解析を行い、当該変数を用いた関数呼出し文における当該変数の値を設定する代入文を探し、もしその代入文における定義が当該関数呼出し文に到達するのであれば、そのような代入文の場所すべてを、呼出し点と組にして関数リストに登録することが可能である。
【００４２】
以上のように、本実施形態によれば、依存解析処理によって関数呼出しに使用される可能性のあるポインタ値をさらに絞り込むことが可能である。したがって、関数リスト９３（図１参照）にリストアップする関数を少なくすることが可能となり、関数ポインタチェック関数の本体の処理も少なくすることが可能となるため、関数ポインタチェック処理の高速化を図ることが可能となる。
【００４３】
（第３の実施形態）
次に、本発明の第３の実施形態について説明する。例えば、Ｃ言語で書かれたプログラムの場合、ファイル中に関数本体がない関数を呼び出すためには、当該ファイル中の関数呼出しの前に関数プロトタイプ宣言を書くことが推奨されている。しかし伝統的には、関数プロトタイプ宣言がなくてもプログラムは動作するため、関数プロトタイプ宣言を書いていない古いプログラムも少なくない。本実施形態では、このような関数プロトタイプ宣言を書いていないプログラムでも正しくチェックコードを生成する方法を説明する。なお、第１の実施形態と共通する部分については、同じ符号を付し、説明を省略する。
【００４４】
図１２は、第３の実施形態におけるコンパイラとリンカの構成を示す図である。図１２に示すように、本構成は、高信頼プログラム生成コンパイラ３０と、高信頼プログラム生成リンカ２０とを含んでいる。高信頼プログラム生成コンパイラ３０は、入力プログラム９０および中間語９１を入力し、チェックコード等を挿入した中間語９１、関数リスト９３および出力プログラム９２を出力する。ここで、出力プログラム９２は、機械語であっても良いし、ソースプログラムであっても良い。高信頼プログラム生成リンカ２０は、出力プログラム９２、中間語９４およびライブラリ関数９６を入力し、チェックコード等を挿入した中間語９４、関数リスト９５および実行プログラム９７を出力する。
【００４５】
図１３は、高信頼プログラム生成コンパイラ３０（図１２参照）の構成を示す図である。高信頼プログラム生成コンパイラ３０は、構文解析部１１と、関数検査・コード生成部１７とを含んで構成されている。構文解析部１１は、図１の高信頼プログラム生成コンパイラ１０と特に変わらないのでこれ以上説明しない。関数検査・コード生成部１７は、中間語９１と関数リスト９３を入力し、チェックコード等を挿入した中間語９１、関数リスト９３および出力プログラム９２を出力する。
【００４６】
図１４は、高信頼プログラム生成リンカ２０（図１２参照）の構成を示す図である。高信頼プログラム生成リンカ２０は、構文解析部１１と、高信頼プログラムリンク時生成部２２と、リンク部２３とを含んで構成される。構文解析部１１は、通常のコンパイラが有する構文解析の機能と特に変わらない。高信頼プログラムリンク時生成部２２は、中間語９４と関数リスト９５とを入力して、チェックコード等を挿入された中間語９４を出力する。また、高信頼プログラムリンク時生成部２２は、関数リスト９５を出力する。リンク部２３は、チェックコード等を挿入された中間語９４とライブラリ関数９６とを入力して、実行プログラム９７を出力する。リンク部２３は、通常のリンカと特に変わらない。
【００４７】
図１５は、関数検査・コード生成部１７（図１３参照）の処理手順を表すフローチャートである。
【００４８】
まず、プログラム中にある関数本体から未処理のものを１つ選ぶ（Ｓ４０１）。プログラム中にinclude文がある場合は、プリプロセッサによってinclude文がすべて展開された後で本処理を行う。ここで、プログラムとは、中間語９１（図１３参照）を意味する。次に、Ｓ４０１で選択した関数本体に対応する関数プロトタイプ宣言を、プログラム中から探す（Ｓ４０２）。次に、Ｓ４０２を行った結果、関数プロトタイプ宣言が見つかった場合（Ｓ４０３でＹｅｓ）は、Ｓ４０４に進み、見つからなかった場合（Ｓ４０３でＮｏ）は、Ｓ４０６に進む。
【００４９】
Ｓ４０４では、プログラム中の関数すべてを処理したかどうかを判定し（Ｓ４０４）、全関数本体を処理した場合（Ｓ４０４でＹｅｓ）は、高信頼プログラム生成処理を行い（Ｓ４０５）、Ｓ４０６に進む。また、未処理の関数本体がある場合（Ｓ４０４でＮｏ）は、Ｓ４０１に戻って処理を継続する。ここで、高信頼プログラム生成処理Ｓ４０５は、第１の実施形態における高信頼プログラム生成部１３（図１参照）の処理、すなわち図６および図７の処理と変わらないので、これ以上説明しない。
【００５０】
Ｓ４０６では、コード生成処理を行い（Ｓ４０６）、処理を終了する。ここで、コード生成処理Ｓ４０６が終了すると、出力プログラム９２（図１３参照）が出力され、高信頼プログラム生成リンカ２０（図１２参照）に渡される。また、コード生成処理Ｓ４０６は、第１の実施形態におけるコード生成部１５（図１参照）の処理と変わらないので、これ以上説明しない。
【００５１】
なお、プログラム中の関数本体に対応するプロトタイプ宣言がすべての関数に対して見つかった場合にのみ、高信頼プログラム生成処理Ｓ４０５を行うこととしたが、高信頼プログラム生成処理Ｓ４０５を行うタイミングはこれに限定されない。例えば、関数本体に対応する関数プロトタイプ宣言が存在する（Ｓ４０３でＹｅｓ）と判定された直後に、高信頼プログラム生成処理Ｓ４０５を行うようにすることも可能である。そうすることで、対応する関数プロトタイプ宣言が存在する関数については、高信頼プログラム生成コンパイラ３０（図１２参照）が関数ポインタチェックコードの挿入を行い、対応する関数プロトタイプ宣言が存在しない関数については、高信頼プログラム生成リンカ２０（図１２参照）が関数ポインタチェックコードの挿入を行うことが可能となる。
【００５２】
図１６は、高信頼プログラムリンク時生成部２２（図１４参照）の処理手順を表すフローチャートである。
【００５３】
まず、プログラム中にチェックコードが挿入されているか調べる（Ｓ５０１）。ここで、プログラムとは、中間語９４（図１４参照）を意味する。チェックコードが見つかった場合（Ｓ５０１でＹｅｓ）は、Ｓ５０４に進む。また、見つからなかった場合（Ｓ５０２）は、リンク時生成処理（Ｓ５０２）を実行し、関数ポインタチェック挿入処理（Ｓ５０３）を実行し、Ｓ５０４に進む。
【００５４】
Ｓ５０４では、コード生成処理（Ｓ５０４）を行い、処理を終了する。ここで、コード生成処理Ｓ５０４が終了すると、実行プログラム９７（図１４参照）が出力される。リンク時生成処理Ｓ５０２の詳細については、後記する。また、関数ポインタチェック挿入処理Ｓ５０３およびコード生成処理Ｓ５０４については、それぞれ第１の実施形態における関数ポインタチェック挿入部１３３（図１参照）およびコード生成部１５（図１参照）と変わらないので、これ以上説明しない。
【００５５】
図１７は、リンク時生成処理Ｓ５０２（図１６参照）の処理手順の詳細を表すフローチャートである。
【００５６】
まず、リンク対象プログラムの関数本体のうち、未処理のものを１つ選ぶ（Ｓ５０２１）。ここで、リンク対象プログラムとは、中間語９４（図１４参照）およびライブラリ関数９６（図１４参照）を意味する。次に、Ｓ５０２１で選んだ関数名を関数リスト９５（図１４参照）に登録する。次に、リンク対象プログラム中の全関数本体を処理したか否かを判定する（Ｓ５０２３）。未処理の関数本体がある場合（Ｓ５０２３でＮｏ）は、Ｓ５０２１に戻って処理を継続する。未処理の関数本体がない場合（Ｓ５０２３でＹｅｓ）は、処理を終了する。
【００５７】
以上のように、本実施形態によれば、リンク時においても関数リストの作成と関数ポインタチェックコードの挿入とを行うことが可能となる。これによって、分割コンパイルを行った場合等、コンパイル時にすべてのプログラムが揃わない場合であっても、関数ポインタ変数に対するバッファオーバフロー攻撃を防御可能なプログラムまたはオブジェクトコードを生成することが可能となる。また、セキュリティチェックによる実行時オーバヘッドを軽減させることが可能となる。
【００５８】
（第４の実施形態）
次に、本発明の第４の実施形態について説明する。悪意あるユーザは、特定の標準ライブラリ関数がもつ脆弱性を利用して攻撃することがしばしばある。また一般に、実行時のチェック処理はプログラムの実行速度を低下させる。そこで、例えば、脆弱性を持つことが分かっている標準ライブラリ関数の呼出しがないプログラムではチェック処理を行わないといった制御処理を行うことで、チェック処理の回数を減らす例を説明する。なお、第１または第２の実施形態と共通する部分については、同じ符号を付し、説明を省略する。
【００５９】
図１８は、第４の実施形態における高信頼プログラム生成部１３（図１参照）の処理手順を表すフローチャートである。
【００６０】
まず、プログラム中にある関数本体から未処理のものを１つ選ぶ（Ｓ６０１）。ここで、プログラムとは、中間語９１（図１参照）を意味する。次に、Ｓ６０１で選択した関数本体中に、特定の関数の呼出し、またはアドレス参照による関数呼出しを含むかどうかを調べる（Ｓ６０２）。どちらも含んでいない場合（Ｓ６０２でＮｏ）は、Ｓ６０３に進む。また、少なくとも一方を含んでいる場合（Ｓ６０２でＹｅｓ）は、Ｓ６０５に進む。ここで、特定の関数とは、例えば、Ｃ言語の標準ライブラリ関数strcpyに代表されるセキュリティ上の脆弱性を含む関数とする。しかし、他の特徴を持つ関数を特定の関数とすることを妨げるものではない。また、アドレス参照がどの関数を指しているかは、一般にはコンパイル時に解析できないので、アドレス参照による関数呼出しがある場合にはチェックコードを生成する。
【００６１】
Ｓ６０３では、Ｓ６０１で選択した関数を処理済として扱う（Ｓ６０３）。次に、全関数が処理済であるか否かを調べ（Ｓ６０４）、全関数が処理済ならば（Ｓ６０４でＹｅｓ）、処理を終了し、未処理の関数があれば（Ｓ６０４でＮｏ）、Ｓ６０１に戻って処理を継続する。
【００６２】
Ｓ６０５では、関数リスト作成処理を行い（Ｓ６０５）、関数ポインタチェック挿入処理を行った後（Ｓ６０６）、処理を終了する。ここで、関数リスト作成処理Ｓ６０５および関数ポインタチェック挿入処理Ｓ６０６は、それぞれ第１の実施形態における関数リスト作成部１３１（図１参照）および関数ポインタチェック挿入部１３３（図１参照）の処理、すなわち図６および図７に示した処理と変わらないので、これ以上説明しない。
【００６３】
なお、本実施形態においては、関数本体中に特定の関数呼出し、またはアドレス参照による関数呼出しが存在する関数が見つかった時点で、それ以降の関数すべてに対して、関数リスト作成処理Ｓ６０５および関数ポインタチェック挿入処理Ｓ６０６を行うこととしたが、関数リスト作成処理Ｓ６０５および関数ポインタチェック挿入処理Ｓ６０６を行うタイミングはこれに限定されない。例えば、全関数を処理したか否かを判定する処理Ｓ６０４を、Ｓ６０３とＳ６０６の直後に行うようにすることも可能である。そうすることで、関数本体中に特定の関数呼出し、またはアドレス参照による関数呼出しが存在する関数に対してのみ、関数リスト作成処理Ｓ６０５および関数ポインタチェック挿入処理Ｓ６０６を行うことが可能となる。
【００６４】
以上のように、本実施形態によれば、例えば、脆弱性を持つことが分かっている標準ライブラリ関数の呼出しがないプログラムではチェック処理を行わないといった制御処理を行うことで、チェック処理の回数を減らすことが可能となる。これによって、チェック処理による実行速度の低下を緩和することが可能となる。
【００６５】
（第５の実施形態）
次に、本発明の第５の実施形態について説明する。バッファオーバフロー攻撃は、関数スタック上のローカル変数に対して行われることが多い。しかし、グローバル変数に対するバッファオーバフロー攻撃も理論上は可能である。本実施形態では、グローバルな関数ポインタ変数に対するバッファオーバフロー攻撃を防御する方法を説明する。なお、本実施形態における関数リスト作成部１３１（図１参照）は、第１または第２の実施形態における関数リスト作成部１３１（図１参照）と同様であるので、説明を省略する。
【００６６】
図１９は、第５の実施形態における関数ポインタチェック挿入部１３３（図１参照）の処理手順を表すフローチャートである。
【００６７】
まず、プログラム中にあるグローバルな関数ポインタ変数のうち、未処理のものを１つ選ぶ（Ｓ７０１）。ここで、プログラムとは、中間語９１（図１参照）を意味する。次に、Ｓ７０１で選択した関数ポインタ変数を用いたすべての関数呼出しに対して、その直前に関数ポインタチェック関数呼出しを挿入する（Ｓ７０２）。次に、プログラム中のすべてのグローバルな関数ポインタ変数を処理したかを調べ（Ｓ７０３）、すべて処理した場合（Ｓ７０３でＹｅｓ）は、処理を終了し、そうでない場合（Ｓ７０３でＮｏ）は、Ｓ７０１に戻って処理を継続する。なお、Ｓ７０３でＹｅｓの場合には、図７に示した関数リストを用いて関数ポインタチェック関数本体を作成し、プログラム中に挿入する処理Ｓ２０５を行う必要があるが、ローカルな関数ポインタ変数の関数ポインタチェック挿入処理において、関数ポインタチェック関数本体の作成がなされていると仮定して、重複を防ぐために、ここでは関数ポインタチェック関数本体の作成処理を省略してある。
【００６８】
以上のように、本実施形態によれば、グローバルな関数ポインタ変数に対するバッファオーバフロー攻撃があった場合でも、関数ポインタ変数に対するバッファオーバフロー攻撃を防御可能なプログラムまたはオブジェクトコードを生成することが可能となる。また、セキュリティチェックによる実行時オーバヘッドを軽減させることが可能となる。
【００６９】
以上、図１〜図１９を用いて説明したように、前記実施形態の言語処理方法によって生成されたプログラムまたはオブジェクトコードは、関数ポインタ変数を用いた関数呼出しの直前で、当該関数ポインタ変数の値が、呼び出される可能性のある関数へのポインタの値と一致するか否かを調べる。そのため、バッファオーバフロー攻撃によって当該関数ポインタ変数の値が書き換えられた場合でも、書き換えられた関数の呼出しを検出し、その関数呼出しの実行を防ぐことが可能であり、コンピュータプログラムのセキュリティ向上に利用することが可能である。
【００７０】
また、前記実施形態の言語処理方法によって生成されたプログラムまたはオブジェクトコードは、関数ポインタ変数を用いた関数呼出し時、すなわち当該変数のレジスタへの読み出し時のみでチェック処理を行うか、または、それに加えて当該関数呼出し点に到達する関数ポインタ変数への定義点で登録処理を行う。従って、関数ポインタ変数へのすべての書き込み時およびすべてのレジスタへの読み出し時にチェック処理を行う場合に比べて、実行時オーバヘッドを軽減することが可能である。
【００７１】
以上、本発明を前記実施形態に基づき詳細に説明したが、本発明は前記実施形態に限定されるものではなく、その要旨を逸脱しない範囲において変更可能である。
【図面の簡単な説明】
【００７２】
【図１】第１の実施形態における高信頼プログラム生成コンパイラの機能例を示す図である。
【図２】第１の実施形態における高信頼プログラム生成コンパイラによる処理を実行する言語処理装置のハードウェア構成の一例を示す図である。
【図３】第１の実施形態における入力プログラムの一例を示す図である。
【図４】第１の実施形態における関数リストの一例を示す図である。
【図５】第１の実施形態における出力プログラムの一例を示す図である。
【図６】第１の実施形態における関数リスト作成部の処理手順を表すフローチャートである。
【図７】第１の実施形態における関数ポインタチェック挿入部の処理手順を表すフローチャートである。
【図８】第２の実施形態における入力プログラムの一例を示す図である。
【図９】第２の実施形態における関数リストの一例を示す図である。
【図１０】第２の実施形態における出力プログラムの一例を示す図である。
【図１１】第２の実施形態における関数リスト作成部の処理手順を表すフローチャートである。
【図１２】第３の実施形態におけるコンパイラとリンカの構成を示す図である。
【図１３】図１２の高信頼プログラム生成コンパイラの構成を示す図である。
【図１４】図１２の高信頼プログラム生成リンカの構成を示す図である。
【図１５】図１３の関数検査・コード生成部の処理手順を表すフローチャートである。
【図１６】図１４の高信頼プログラムリンク時生成部の処理手順を表すフローチャートである。
【図１７】図１６のリンク時生成処理の処理手順の詳細を表すフローチャートである。
【図１８】第４の実施形態における高信頼プログラム生成部の処理手順を表すフローチャートである。
【図１９】第５の実施形態における関数ポインタチェック挿入部の処理手順を表すフローチャートである。
【符号の説明】
【００７３】
１０    高信頼プログラム生成コンパイラ
１１    構文解析部
１３    高信頼プログラム生成部
１５    コード生成部
９０    入力プログラム
９１    中間語
９２    出力プログラム
９３    関数リスト
９７    実行プログラム
１３１  関数リスト作成部
１３３  関数ポインタチェック挿入部
５２０  言語処理装置
５２４１  処理部
５２４２  記憶部
【図１】
2007041777.tif 000003
【図２】
2007041777.tif 000004
【図３】
2007041777.tif 000005
【図４】
2007041777.tif 000006
【図５】
2007041777.tif 000007
【図６】
2007041777.tif 000008
【図７】
2007041777.tif 000009
【図８】
2007041777.tif 000010
【図９】
2007041777.tif 000011
【図１０】
2007041777.tif 000012
【図１１】
2007041777.tif 000013
【図１２】
2007041777.tif 000014
【図１３】
2007041777.tif 000015
【図１４】
2007041777.tif 000016
【図１５】
2007041777.tif 000017
【図１６】
2007041777.tif 000018
【図１７】
2007041777.tif 000019
【図１８】
2007041777.tif 000020
【図１９】
2007041777.tif 000021