SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

実例で学ぶ脆弱性対策コーディング

Linuxのカーネルに潜む脆弱性をつぶすパッチ

実例で学ぶ脆弱性対策コーディング 第5回

  • X ポスト
  • このエントリーをはてなブックマークに追加

脆弱性の解説:NULLポインタ参照の脆弱性とコンパイラによる最適化

 この関数のなかではtunの値がNULLであるかどうかのチェックが行われていますが、その前にポインタskの初期値としてtun->skの値が使われています。この値の参照とNULLチェックの順序がマズイことに気づけるかどうかが今回のポイントです。

 今回のコードでは、2つの問題があります。これを順番に説明していきましょう。

Nullポインタ参照の脆弱性

 まず1つ目はNULLポインタ参照の脆弱性です。この関数のなかでは、引数の一つであるfileをたぐってtunデバイスの情報を持つ構造体tunを取り出しています。有効な値を取り出せているかどうかを確認するため、tunの値がNULLでないかどうかをチェックしています。しかし、NULLチェックの前に、ローカル変数skを初期化するためにtunの値を使ってしまっています。

struct sock *sk = tun->sk;

 もし、tunの値がNULLポインタだった場合、このコードではNULLポインタを参照することになります。

 NULLポインタを参照すると、多くの場合、アクセス違反が発生してプログラムは異常終了するでしょう。この関数はカーネルの一部として実行されるデバイスドライバのコードの一部です。ユーザー空間のプログラムと違って、デバイスドライバでアクセス違反が発生するとカーネルごとクラッシュしてしまうことになります。しかも、tunデバイスはユーザー空間のプログラムとイーサネットなどのネットワークインターフェイスの間にたってデータのやりとりを行うことを想定しています。そのデバイスドライバに問題があれば、ネットワーク越しに悪意のあるデータを送信して処理させることで、サービス運用妨害(DoS:denial-of-service)攻撃に悪用される危険が高くなります。

 さらに、ARMやXScaleのようなアーキテクチャでは、NULLポインタ参照の脆弱性を悪用してコード実行が可能になるという研究があります。これらのアーキテクチャでは、0番地のアドレスがメモリ上にマップされているのみならず、Exception Vector Tableと呼ばれる特別な意味を持っています。Supervisor(SVC)モードで実行されるリアルタイムOSではメモリアクセスが制限されておらず、0番地へのアクセスが可能となり、これを悪用してコード実行が可能になるというものです。これは米ジュニパーネットワークスの研究者により発表され、話題になりました。興味のある方は、巻末の参考情報に挙げたBarnaby Jack氏の「Vector Rewrite Attack」(PDF)を読むと良いでしょう。

 じつはARMやXScaleのようなアーキテクチャでなくても、Linuxで同様の攻撃手法を適用することが可能です。システム設定の内容にも依存しますが、多くのLinuxシステムで使われている設定において、NULLポインタ参照の脆弱性を悪用してコード実行につながる攻撃が可能であるということが発見されたのです。

コンパイラの最適化に関する問題

 ここで2つ目の問題です。じつは今回のようなコードでは、if(!tun)のチェックがコンパイラの最適化によって削除されてしまうことがあります。

 NULLポインタ参照はCの仕様上、未定義の動作(undefined behavior)に該当します。未定義の動作となるコードをどのように扱うかはコンパイラに任されており、例えば、未定義の動作となるような状況は発生しないという前提で、より高速なコードを生成するために最適化の対象とすることが許されています。コンパイラgccの最適化では、まさにこのような処理を行っています。

 今回のコードでは、if(!tun)でtunがNULLポインタになっていないかどうかをチェックしています。しかし、その前に既にtun->skという形でtunの値を参照しています。if(!tun)まで実行が正常に行われているということはtunにはNULLでない有効な値がはいっており、if(!tun)によるチェックは必ず素通りするはずです。そこでgccの最適化では、if(!tun)の部分を不要なコードとして削除してしまうのです。

 これはプログラマの意図に反した、望ましくない最適化です。コードレベルではきちんとエラーチェックを行っているつもりでも、コンパイラが出力したオブジェクトコードではその部分が抜け落ちてしまうのですから、通り一遍のコードレビューでは問題を発見できません。

 コードが未定義の動作となるかどうかをコードレビューで発見するのは容易なことではありません。Cの言語仕様にしたがい、可搬性のある(portableな)コードを書くことは、このような観点からも重要だと言えます。

 以上2つの問題により、NULLポインタ参照を起こすコードから、アクセス違反で停止せずに実行が継続し、攻撃者が用意したコードを実行してしまうといったことが可能になります。

実際の修正内容

 さて、それでは次にこの脆弱性の修正がどのように行われたかを見てみましょう。NULLポインタ参照の脆弱性、そしてコンパイラの最適化にまつわる問題、この2つを理解していれば、コードの修正は自ずと明らかになります。NULLポインタチェックをまず最初に行うこと、値を参照するのはその後とするよう、順序を修正していることが分かります。

-   struct sock *sk = tun->sk;
+   struct sock *sk;
    unsigned int mask = 0;

    if (!tun)
        return POLLERR;

+   sk = tun->sk;
+
    DBG(KERN_INFO "%s: tun_chr_poll\n", tun->dev->name);

 この脆弱性はCVE-2009-1897としてCVEに登録されています。

次のページ
コンパイラの最適化と脆弱性にまつわる事例

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
実例で学ぶ脆弱性対策コーディング連載記事一覧

もっと読む

この記事の著者

久保 正樹(JPCERT コーディネーションセンター)(クボ マサキ(JPCERT コーディネーションセンター))

脆弱性アナリストJPCERTコーディネーションセンター慶応義塾大学環境情報学部卒。ソニーでデスクトップPCのソフトウェア開発に携わったのち、米国ダートマス大学にてオーディオ信号処理、電子音響音楽の研究を行い、電子音響音楽修士を取得。2005年4月よりJPCERTコーディネーションセンターにて、脆弱性...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

戸田 洋三(JPCERT コーディネーションセンター)(トダ ヨウゾウ(JPCERT コーディネーションセンター))

リードアナリストJPCERTコーディネーションセンター東京工業大学情報理工学研究科修士課程修了。学生時代は、型理論および証明からのプログラム抽出を研究。その後、千葉大学総合情報処理センターのスタッフとして、学内ネットワークの運営、地域ネットワーク、IPマルチキャストの実験ネットワークであるJP-MB...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/5241 2010/07/06 14:00

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング