開発者は、セキュリティ上の脆弱性と品質に関わる欠陥を含む重大な不具合に関して強力で正確な分析をローカルに実行し、不具合のないコードをチェックインできます。 Klocwork Insightでは手続き間の制御フロー、データフロー、バリューレンジの伝播(value-range propagation)、シンボリック ロジックテクノロジーを適用して、可能性がある実行パスを入念に検証し、数百種類ものエラーを発見します。
メモリやリソースのリーク
割り当てが開放されたメモリの使用
メモリの不正な割り当て解除
NULL ポインタの逆参照
初期化されていないデータの使用
リソース管理
競合違反
アーキテクチャの違反
ヘッダーファイル構造
デッドコード
到達不能コード
使用されることのない計値
未使用の関数パラメータ
未使用のローカル変数
バッファオーバーフロー
無効なユーザ入力
SQL インジェクション
パスインジェクション
ファイルインジェクション
クロスサイトスクリプティング
情報漏えい
脆弱な暗号化
脆弱なコーディング
C から派生した言語で生み出される不具合の多くは、すべての派生言語はもちろん、元になる C言語自体にも共通するものです。最も発生しがちな問題は、C ではプログラマが明示的にメモリを管理する必要がある点に起因します。これはオリジナルコードの開発者にとっても、その後コードを引き継ぐすべての開発者にとっても、保守においてずっと付きまとう負担となります。 Klocwork Insight が検出する一般的な C および C++ のエラーには、次の 3 種類があります。
NULL ポインタ(または Java における nullオブジェクト参照)は、一般にコード内での対策が不十分であるため、逆参照された場合には数々の問題を引き起こします。この種のエラーが入り込む典型的な例は、大規模なシステムで別々の開発者が相互に依存するモジュールを担当する場合です。たとえば、次の例を見てください。
void foo(int* p) { *p = 32; }
void bar() { foo(NULL); }
これらの関数が両方とも、同じモジュールまたは同じサブシステム内に物理的に存在している場合には、経験が浅い開発者でもエラーを発見するのは容易でしょう。しかし、これらの関数が別々の開発者または別々の開発チームによって書かれる独立したサブシステム内に分かれている場合、このようなエラーを手作業で発見するのは非常に困難です。 値が投影されると、さらに複雑さが強まります。
void foo(int* p) {
*p = 32;
}
void bar(int x) {
int* p = NULL;
if( x == 15 || x == 20 )
p = &x;
if( x > 10 && x <= 20 )
foo(p);
}
この例では、入力パラメータの特定の値がエラーを引き起こしますが、それ以外の値はエラーとなりません。Klocwork Insight 製品は、こうした値がコード パスの利用可能な空間に及ぼす影響を理解します。
バッファ オーバーフローは、より一般的には「配列範囲違反」とも呼ばれるもので、データ オブジェクトで定義されている範囲を超えてコードが配列の要素をアドレス指定する場合に発生します。たとえば、次の例を見てください。
char arr[32];
for( int i = 0; i < 64; i++ )
arr[i] = (char)i;
この単純な例では、プログラマが明らかに、スタックベースの変数 "arr"の範囲を超えるメモリをアドレス指定しています。これはメモリの上書きを引き起こし、そこには関数が呼び出し側に正しく戻るために必要なスタックフレーム情報などが含まれている可能性があります。 このようなコーディング パターンは、今日のソフトウェアに存在するセキュリティ上の脆弱性としては最も有害なものの 1 つといっても過言ではありません。 脆弱性の詳細は事例ごとに異なりますが、根底にある問題は同じです。すなわち、配列コピー操作が誤っているか、セキュリティ上の弱点に対するガードが不十分なことです。次の例を考えてみてください。
void foo(unsigned char* data) {
unsigned char value[32];
int len = )data [0];
memcpy(value, data + 1, len);
}
"memcpy" のコールは、最大 256 バイト("data" バッファのゼロ番目の要素をその長さとして取る結果)を 32バイトしかないスタックベースの固定サイズ配列にコピーすることになります。このデータストリームを外部から利用しうる場合、システムはハッキングされる可能性があります。 このバッファ オーバーフローというエラーがスタックを破壊してコードインジェクションに対する脆弱性をもたらすという数多くの事例の中でも特に注目すべきものとしては、詳細な文書が公表されているマイクロソフトのアニメーション カーソル ファイルに関する問題を参照してください。
上にある NULL ポインタ逆参照の例で指摘したように、C から派生した言語で最も大きな問題の 1つは、プログラマがメモリを完全に管理する必要があることです。ヒープに割り当てられたバッファや構造体は、参照されなくなった時点で適切に解放しなければなりません。
次に単純な例を示します。
void foo() { malloc(32); }
この関数が返ったあと、長さ 32 バイトのデータ ブロックがヒープ上に参照されないまま残ります。これが一定の回数繰り返されれば、ヒープ マネジャが機能しなくなります。 Klocwork Insight 製品は制御フローとデータ フローを厳密に分析することにより、手作業の検査では容易に見落としてしまうようなコード構造内のメモリ リーク発生箇所も特定できます。たとえば、次の例を見てください。
typedef struct List {
struct List* next;
char* value;
int len;
} List;
void addToList(List* root, char* str) {
List* elem = (List*)malloc(sizeof(List));
if( elem ) {
elem->next = root->next;
root->next = elem;
/* Duplicate the string, allocating memory */
elem->value = strdup(str);
elem->len = strlen(str);
}
}
void removeList(List* root) {
List* ptr;
while( (ptr = root) != NULL ) {
root = root->next;
/* This releases the structure, but not the string */
free(ptr);
}
}
void foo() {
List* root = (List*)calloc(1, sizeof(List));
addToList(root, "hello");
addTolist(root, "world");
removeList(root);
free(root);
}
ここでは 1つずつリンクされるリストがクリーンアップされるたびに、メモリのリークが発生します。リストの要素自体は解放されますが、各要素が指示する文字列は解放されないからです。この種のエラーは、基底クラスと派生クラスを持つ C++ で異なる要素を定義し、メモリリークがないことを保証するためには仮想デストラクタ チェーンによってすべてをクリーンアップしなければならない場合には、特に危険です。
Java プログラミングにも、よく見られる固有のエラー脆弱性が存在します。次の 3 種類は、Klocwork Insight 製品で検出可能な、多くの Java コードが抱える欠陥の例です。
マルチコアまたはマルチ CPU のハードウェア環境が普及してきたことから、並列プログラミング、すなわちプログラムの実行コンテクストを 2つ以上のスレッドに分割する処理方法が一般化しつつあります。ソースコード分析エンジンが並列コンテクストを本格的に取り扱おうとすれば、使用するプログラミング言語には関係なく、いくつかの基本的な要件が課されることになります。最も重要な点としては次の 2 つが挙げられます。
デッドロックまたはライブロック状況を特定する能力 競合状況を特定する能力
こうした機能がなければ、プログラマが自分で、実行時におけるプログラムの動作を推測するしかなくなります。 デッドロックとライブロックは、2つ以上の実行スレッドがコードの特定セクションに同時にアクセスしないように保護するため、プログラムがロッキング動作を使用する状況を意味します。典型的にはグローバル データの変更に関する場合となりますが、そうしたコンテクストに限定されるわけではありません。 次の例を考えてみてください。
public void foo()
{
if( someCondition() ) {
synchronized( someGlobalSemaphore ) {
// First problem, this blocks all threads
Thread.Sleep(1000);
if( someOtherCondition() ) {
// Second problem, potential contention
synchronized( someOtherGlobalSemaphore ) {
...
}
}
}
}
}
ここでは、複数の異なるロッキング シナリオが示されており、そのいずれもがたとえば次のように、ブロック状況を発生させる可能性があります。
プロセス規模のロックを保持しながら 1 つのスレッドを明示的にブロックする
潜在的に両立しえない取得動作を通じて競合をロックする
「競合状況」が引き起こす、ほとんど再現不可能な挙動を追跡する場合も、膨大なデバッグ時間を費やすことになりかねません。この状況が発生するのは、2つ以上のスレッドそれぞれに、他のコンテクストにあるデータを変更する同等の機会がある場合です。単純な例としては、J2EEコンテナ内で実行されるサーブレットのような、再入可能クラスの静的データが挙げられます。あるスレッドのクラスデータを変更する際、それが別のスレッドによって同時に読み取られるか、別の形で変更される可能性がある場合には、想定外の挙動となります。
メモリ リークと同じく、リソースリークもアプリケーションにとっては致命的となりうるもので、時間が経過すればサービス拒否状況に至ります。リソースリークの中でも最も憂慮すべき種類は、オペレーティングシステムのハンドルや記述子、あるいは明示的な解放動作が必要なフレームワークメモリと結び付いたものです。 Klocwork Insight では、ファイル記述子やストリームなどの基本的なタイプから、フレームワーク固有の環境用動作(Google WebToolkit、Struts、Java Mail、J2ME、ImageIO、Hibernateなど各種)まで、多彩なリソース動作をサポートしています。 よく見られる誤りは、ランタイム ガーベッジコレクタ(GC)がメモリ参照と同様にリソースについても面倒を見てくれると勘違いすることです。しかし、オブジェクト自体に関連付けられたメモリはGC が収集しますが、オブジェクトに関連付けられたリソースはクリーンアップされません。たとえば、次の例を見てください。
public void foo(String name) throws IOException {
Reader r = new InputStreamReader(new FileInputStream(name));
char ch;
while( (ch = r.read()) != -1 ) {
if( ch == ' ' )
return;
}
}
この例の入力ストリーム オブジェクトについて収集する必要があるリソースとしては、少なくとも次の 2 種類があります。
ストリーム オブジェクト自体、およびストリーム内部の状態管理に関連付けられたメモリ
ファイル システム内の元になるファイルに関連するオペレーティング システムのファイル記述子またはハンドル
GC は第1の側面については面倒を見ますが、元になる記述子は開いたままにします。そのため、時間の経過とともに貴重なシステム リソースが消費されていき、最終的にはサービス拒否状況に至ることになります。
Web アプリケーションの開発には陥りやすい落とし穴が数多く潜んでいることから、デバッグ自動化の研究においては急速に、最も一般的な領域となりつつあります。Klocwork Insight のソース コード分析製品は、次のような脆弱性を検出します。
SQL インジェクション
プロセスまたはファイル インジェクション
コード インジェクション
クロスサイト スクリプティング(XSS)
リクエスト捏造
こうした脆弱性を生む誤りについては、それぞれ特定のチェックと分析が必要になります。とはいえ簡単に言えば、こうした脆弱性を狙う攻撃の多くは、防御が手薄な設計に付け込んで不正なデータを送り込むものと一般化できます。つまり、ユーザや別のプロセスから入力として受け取ったデータを、フォーマット、値の範囲、その他そのデータ タイプについて意味がある特性を厳格に検証しないまま使用する甘さに付け込むわけです。
たとえばサーブレットのコンテクストでは、次のスニペットを考えてみてください。
public void doGet(HttpServletRequest req,
HttpServletResponse res)
{
String name = req.getParameter("username");
String pwd = req.getParameter("password");
// SQL Injection
int id = validateUser(username, password);
// XSS
String retstr = "User : " + name + " has ID: " + id;
res.getOutputStream().write(retstr.getBytes());
}
private int validateUser(String user, String pwd)
throws Exception
{
Statement stmt = myConnection.createStatement();
ResultSet rs;
rs = stmt.executeQuery("select id from users where user='" +
user + "' and key='" + pwd + "'");
return rs.next() ? rs.getInt(1) : -1;
}
この例には、さまざまな種類のよく見られるエラーが含まれています。明らかなリソース リークや管理上の問題を別にしても、次のようなものが挙げられます。
入力 URL パラメータをフィルタリングせずにユーザ名およびパスワードとして使用することによる SQL インジェクションの危険性
入力 URL パラメータをフィルタリングせずにユーザへの応答として含めることによるクロスサイト スクリプティングまたはリクエスト ミラーリングの危険性
悪意のあるユーザは適切にマークアップされた URLパラメータを与えることにより、多くの問題を引き起こすことができるでしょう。同様に、最終的にファイル名として使用される文字列かどうかの検証を怠るアプリケーションも、ファイルまたはプロセス インジェクションに対して無防備ということになります。 Klocwork Insight では総合的な Web 脆弱性分析の結果、静的ソース コード分析を通じて発見可能な OWASP トップ 10 脆弱性のすべてを検出することができます。
入力の未検証
アクセス制御の破綻
認証およびセッション管理の破綻
クロスサイト スクリプティング
バッファ オーバーフロー
インジェクションを許す欠陥
不適切なエラー処理
セキュアでないストレージ
アプリケーション起因のサービス拒否
Klocwork Insightではソースコードのグラフィカルモデルを活用して、ソフトウェア設計者がシステムに悪影響を及ぼすことなく、さまざまな最適化モデルを試すことができます。この自動コード検出機能を通じ、コードの複雑な相互関係を視覚化して理解し、what-ifシナリオを作成して、理想的な最適化に向け徐々にコードをリエンジニアリングしていくことができます。 Klocwork Insightの重大不具合検出機能と組み合わせることで、より優れた保守性の高いコードを開発することが可能になります。
システムビューが既存アプリケーションの物理的構造を明示します。アプリケーション内部の依存関係はもちろん、アプリケーションと外部環境との依存関係も明らかになります。 アーキテクチャ上の複雑な問題(たとえば、循環依存性やモデル改良など)を迅速に発見し、開発者向けに対応可能な変更の一覧リストを作成することができます。
多くの不要なincludeを含む過度に複雑なヘッダファイル構造を伴ったシステムに対して自動分析を実行して、システム規模とビルド時間の削減可能量を推測するとともに、ヘッダファイル最適化の具体的な特定の推奨ロードマップを提供します。 自動フローチャート機能を活用してファイルのプロセスフローを理解すれば、手作業でのコードレビューを効果的に進めることができます。
総合的なウェブレポーティングソリューションにより、開発チームにおいて、欠陥修正率などの重要なソフトウェアメトリックをデスクトップで追跡管理できるようになります。
ソフトウェア開発組織ではソフトウェア開発プロセスを管理するため、多種多様なメトリックを収集します。Six SigmaやCMMIその他、成熟したソフトウェア開発組織が使用するプロセスでは特に、プロセスメトリック、リソースメトリック、環境メトリックなどを収集します。Klocwork Insightは、こうしたメトリック収集機能をさらに強化し、ソフトウェアコードから直接引き出される、客観的で実用性が高い製品メトリックを100種類以上も提供します。
強力な拡張フレームワークを通じて分析機能を拡張できるため、開発者はスタイルやパスに関する独自の分析チェッカを記述し、コーディングや組織上の特定の要件に準拠することができます。
まったく新しい宣言型言語によって、いかなるコードベースについてもC、C++、Javaの分析を完全にカスタマイズできるようになりました。この高水準言語により、独自のチェッカを追加してKlocworkが内蔵するライブラリを拡張し、組織、規制、コードベースに関する特定の要件に準拠することができます。また、Klocworkが運営する開発者フォーラムChecker Exchangeにアクセスし、チェッカのオープンソースライブラリからダウンロードしたり、自作チェッカを投稿していただけるようにもなりました。このライブラリに投稿されたチェッカはすべて、コミュニティのメンバが自由にダウンロードできるため、Klocwork Insightのお客様の間で不具合の特定に関するベストプラクティスを共有していただけます。
ASTベースチェッカは、チェッカがエラー状況の評価にランタイム状態を必要とせず、ソースツリー内全体で発見可能な場合に使用します。 ASTチェッカ例: クラスでは、コンストラクタまたはデストラクタ内の仮想メンバ関数に対するコールを配置してはならない データフローチェッカは、効果的な分析を行うため、コードのランタイム状態と関数コール境界をまたぐ複雑なデータフローに関する知識を必要とします。 データフローチェッカ例: Arrays.asList()メソッドが返すコレクションは不変であって変更不可― 変更しようとするとランタイム時例外が発生する
分析は、好みのIDE、テキストエディタ、コマンドライン環境による開発者の自然な作業環境内で実行可能です。
Klocwork Insightでは以下のIDEについてプラグインをサポートしています。
Carbide.c++ 2.0, 2.1
Eclipse 3.4, 3.5, 3.6
Microsoft Visual Studio 2005(*), 2008(*), 2010(*) *但し、Express editionはサポート対象外
IBM Rational Application Developer 7.5.x - Ready for Rational certified
IntelliJ IDEA 8.x, 9
Wind River Workbench 3.1, 3.2
QNX Momentics 6.4.x, 6.5
また、Klocwork Insightは次の環境とも正常に動作します。
Gvim, Emacs, Visual SlickEdit, Platform Builder, KDevelop, Freescale CodeWarrior