静的解析とは、プログラムを実行せずにソースコードを検査して、バグや脆弱性の疑いを見つける手法です。コンパイラの警告と何が違うのか、ツールによって結果がどれほど変わるのかは、同じコードを通した出力を見るのが一番早いので、この記事ではApple clang 21.0.0とCppcheck 2.22.0の実際の出力を並べます。そのうえで、Coverityをはじめとする商用ツールのメーカーと、無料ツールの言語別の選び方を整理します。
まとめ
- 静的解析はコードを動かさずに行う検査です。解析器は分岐ごとに経路を仮定し、変数の状態を追って欠陥を指摘します
- 7つの欠陥を仕込んだ同じCコードで、コンパイラの警告が拾ったのは2つ、clangの解析モードは6つ、cppcheckは6つでした。clangだけがメモリリーク、cppcheckだけが配列の範囲外読み取りを報告しており、ツールごとに見つかる欠陥が違います
- clangの解析モードもcppcheckも、欠陥を見つけたまま終了コード0で終わります。CIで止めるにはcppcheckなら
--error-exitcodeの指定が要ります - 商用の代表はCoverity(Black Duck)、Perforce QACとPerforce Klocwork(Perforce)、Polyspace(MathWorks)、CodeSonar(AdaCore)、Parasoft C/C++testです。機能安全規格への対応、大規模なC/C++、監査用レポートに加え、多言語の脆弱性検出や組織全体の指摘管理も選定理由になります
- それ以外の用途なら、処理系に同梱された解析器かOSSのツールから始め、自社コードで出た指摘の量を見てから商用を検討する順番が損をしません
静的解析とは|コードを実行せずに欠陥を探す検査
静的解析は、ソースコードや中間表現を対象に、規約違反・欠陥・脆弱性の疑いを機械的に洗い出す検査です。テストと違って入力値も実行環境も要らないため、コンパイルが通った時点で、ツールによっては編集中のエディタ上でも結果が出ます。
静的解析・静的コード解析・静的テストの呼び分け
3つの語は混同されがちですが、範囲が違います。ISTQBのシラバスの分類では、静的テストにレビューと静的解析の両方が含まれます。静的解析はツールによる検査で、ソースコードのほか設計モデルなども対象になります。静的コード解析は対象をソースコードに限った呼び方です。静的テストはソフトウェアテストの分類語で、人が読むレビューも含みます。テスト工程の分類として静的テストと動的テストを比べたい場合は静的テストと動的テストの違い|静的解析・動的解析で見つかるバグを同じコードで比較で扱っています。
静的解析の種類:コード解析・メトリクス・SCA・バイナリ
静的解析と関連ツールの主な領域を次に示します。SASTは静的コード解析と重なるため、互いに独立した分類ではありません。
| 種類 | 見る対象 | 主な出力 |
|---|---|---|
| 静的コード解析 | ソースコードの経路・値 | ヌル参照・リーク・規約違反 |
| SAST | 外部入力から危険な処理までの経路 | SQLインジェクション等の脆弱性 |
| メトリクス解析 | 関数の複雑さ・行数・依存 | 複雑度などの数値 |
| SCA | 依存ライブラリと版 | 既知脆弱性・ライセンス |
| バイナリ解析 | コンパイル後の実行ファイル | ソースが無い部品の欠陥 |
この記事で扱うのは1行目の静的コード解析が中心です。SASTはその中でもセキュリティに目的を絞ったもので、詳しくはSASTとは?DAST・SCAとの違いとCI/CDへの組み込み・導入判断を実装視点で解説にあります。依存ライブラリを見るSCAはSCAとは?ソフトウェア構成分析の仕組みとSAST・SBOMとの違い・導入判断を解説、複雑度の数値の読み方はサイクロマティック複雑度とは?計算方法・目安10の根拠とツールで値が違う理由で解説しています。
静的解析ツールとコンパイラの警告の違い
「警告を全部有効にすれば静的解析は要らないのでは」という疑問には、同じコードを両方に通した結果が答えになります。検査対象は次のCコードです。flagが0のとき、pはNULLのまま参照されます。
#include <stdlib.h>
int set_first(int flag) {
int *p = NULL;
if (flag) {
p = malloc(sizeof(int));
}
*p = 1;
return *p;
}
まずコンパイラに、よく使われる警告オプションを付けて通します。
$ clang -c -Wall -Wextra null.c -o /dev/null
$
何も出ません。次に、同じclangを解析モード(Clang Static Analyzer)で実行します。
$ clang --analyze -Xclang -analyzer-output=text null.c
null.c:8:8: warning: Dereference of null pointer (loaded from variable 'p') [core.NullDereference]
8 | *p = 1;
| ~ ^
null.c:4:5: note: 'p' initialized to a null pointer value
4 | int *p = NULL;
| ^~~~~~
null.c:5:9: note: Assuming 'flag' is 0
5 | if (flag) {
| ^~~~
null.c:5:5: note: Taking false branch
5 | if (flag) {
| ^
null.c:8:8: note: Dereference of null pointer (loaded from variable 'p')
8 | *p = 1;
| ~ ^
null.c:9:5: warning: Potential leak of memory pointed to by 'p' [unix.Malloc]
9 | return *p;
| ^
null.c:5:9: note: Assuming 'flag' is not equal to 0
5 | if (flag) {
| ^~~~
null.c:5:5: note: Taking true branch
5 | if (flag) {
| ^
null.c:6:13: note: Memory is allocated
6 | p = malloc(sizeof(int));
| ^~~~~~~~~~~~~~~~~~~
null.c:9:5: note: Potential leak of memory pointed to by 'p'
9 | return *p;
| ^
2 warnings generated.
Clang Static Analyzerの経路探索と値の追跡
出力の「Assuming 'flag' is 0」「Taking false branch」が、解析器の動き方をそのまま示しています。解析器はソースコードを構文木に変換して制御フローを組み立て、分岐の片側ずつを別の経路として、変数がどの値を持ちうるかを追います。flagが0の経路ではヌル参照、0以外の経路ではmallocした領域を解放しないまま関数を抜けるリーク、と2つの欠陥を経路つきで名指ししているのはそのためです。
コンパイラの警告にも制御フローやデータフローに基づく検査があります。今回のApple clangと警告オプションでは、このヌル参照とメモリリークを検出しませんでした。検出範囲はコンパイラの版や設定によって変わります。次の章で見るとおり、逆に警告だけが拾う欠陥もあるので、警告と解析はどちらも有効にしておくのが前提です。
同じコードを3つの解析器に通した結果
ツール選びの記事の多くは機能の一覧を並べますが、実際に何が見つかるかはツールで違います。そこで、典型的な欠陥を7つ仕込んだCファイルを、コンパイラの警告・clangの解析モード・cppcheckに通しました。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int set_first(int flag) { /* ヌル参照とメモリリーク */
int *p = NULL;
if (flag) {
p = malloc(sizeof(int));
}
*p = 1;
return *p;
}
int last_item(void) { /* 配列の範囲外読み取り */
int buf[10];
for (int i = 0; i < 10; i++) buf[i] = i;
return buf[10];
}
int read_header(const char *path) { /* ファイルを閉じずに返る */
FILE *fp = fopen(path, "r");
if (fp == NULL) return -1;
char line[64];
if (fgets(line, sizeof line, fp) == NULL) return -1;
fclose(fp);
return (int)strlen(line);
}
int use_after_free(void) { /* 解放後の参照 */
char *s = malloc(8);
if (s == NULL) return -1;
strcpy(s, "abc");
free(s);
return s[0];
}
int uninit(int flag) { /* 未初期化変数 */
int v;
if (flag) v = 1;
return v;
}
int ratio(int total) { /* ゼロ除算 */
int count = 0;
return total / count;
}
各ツールの出力から、指摘の行だけを抜き出したものが次です。
$ clang -c -Wall -Wextra bugs.c -o /dev/null 2>&1 | grep warning:
bugs.c:17:12: warning: array index 10 is past the end of the array (that has type 'int[10]') [-Warray-bounds]
bugs.c:39:9: warning: variable 'v' is used uninitialized whenever 'if' condition is false [-Wsometimes-uninitialized]
$ clang --analyze bugs.c 2>&1 | grep warning:
bugs.c:10:8: warning: Dereference of null pointer (loaded from variable 'p') [core.NullDereference]
bugs.c:11:5: warning: Potential leak of memory pointed to by 'p' [unix.Malloc]
bugs.c:24:55: warning: Opened stream never closed. Potential resource leak [unix.Stream]
bugs.c:34:12: warning: Use of memory after it is freed [unix.Malloc]
bugs.c:40:5: warning: Undefined or garbage value returned to caller [core.uninitialized.UndefReturn]
bugs.c:45:18: warning: Division by zero [core.DivideZero]
$ cppcheck -q --enable=warning --template='{file}:{line}: {severity}: {message} [{id}]' bugs.c
bugs.c:17: error: Array 'buf[10]' accessed at index 10, which is out of bounds. [arrayIndexOutOfBounds]
bugs.c:24: error: Resource leak: fp [resourceLeak]
bugs.c:34: error: Returning/dereferencing 's' after it is deallocated / released [deallocret]
bugs.c:10: warning: Possible null pointer dereference: p [nullPointer]
bugs.c:11: warning: Possible null pointer dereference: p [nullPointer]
bugs.c:45: error: Division by zero. [zerodiv]
bugs.c:40: warning: Uninitialized variable: v [uninitvar]
欠陥ごとに検出の有無をまとめると、次のようになります。
| 仕込んだ欠陥 | -Wall -Wextra |
clang --analyze |
cppcheck 2.22.0 |
|---|---|---|---|
| ヌル参照 | なし | 検出 | 検出 |
| メモリリーク | なし | 検出 | なし |
| 配列の範囲外読み取り | 検出 | なし | 検出 |
| ファイルの閉じ忘れ | なし | 検出 | 検出 |
| 解放後の参照 | なし | 検出 | 検出 |
| 未初期化変数 | 検出 | 検出 | 検出 |
| ゼロ除算 | なし | 検出 | 検出 |
clangとcppcheckの検出差と併用判断
7つの欠陥のうち、clangの解析モードは6つ、cppcheckも6つを報告しましたが、取りこぼした欠陥が違います。clangは既定のチェッカー構成で配列の範囲外読み取りを報告せず、cppcheckは同じ関数でヌル参照を2行分報告した一方でメモリリークを報告しませんでした。逆に、コンパイラの警告は配列の範囲外と未初期化変数を拾っています。1つのツールの結果を「静的解析で問題なし」と読み替えるのは危険で、少なくともコンパイラの警告と解析器1本は併用し、C/C++なら性格の違う解析器をもう1本足すと取りこぼしが減ります。
なお、この比較は小さな関数単位の欠陥を見たもので、ファイルや関数をまたぐ欠陥での検出差は測定していません。関数をまたいで値を追う深さは製品ごとに違うので、その差は自社コードで確かめる必要があります。
既定の終了コードとcppcheckのCI失敗設定
上の3つの実行は、どれも指摘を出したうえで終了コード0で終わりました。CIにそのまま組み込むと、指摘が出てもビルドは成功のままです。cppcheckは --error-exitcode を指定すると、指摘があったときの終了コードを変えられます。
$ cppcheck --enable=warning --error-exitcode=1 -q bugs.c >/dev/null 2>&1; echo $?
1
導入の初日に決めるべきは、細かなルールの取捨より「どの指摘でCIを止めるか」です。既存コードの大量の指摘を基準値として切り離し、新しく書いた差分から止める運用は静的コード解析の誤検知と限界|レビュー・テストとの責任分担で手順を説明しています。
静的解析で見つかる欠陥と残る欠陥
静的解析が得意なのは、コードの形と経路から判定できる欠陥です。上の表の7種類に加え、未使用の変数や関数、型の不整合、命名規則の逸脱、外部入力を連結したSQL文のような既知の脆弱パターンが該当します。
一方で、入力や外部システムの振る舞いを解析器が十分にモデル化できない場合は、関連する欠陥を見逃すことがあります。仕様の妥当性や実環境での性能を確かめるには、レビューや動的な検証も必要です。どこまで網羅的に調べるかと、誤検知をどこまで許すかは、ツールの設計思想で分かれます。MathWorksのPolyspaceはこの2つを別製品にしており、Bug Finderについて「Bug Finder does not aim for exhaustive analysis. It tries to detect as many bugs as possible and reduce false positives.」、Code Proverについて「The software aims for zero false negatives.」と公式ドキュメントで説明しています。Code Proverの保証は、対象とする実行時エラーの種類と、解析オプションで与えた実行条件の範囲内に限られます。エラーがないと証明できない箇所は、人による確認が必要です。誤検知が生じる理由と、レビュー・テストとの分担は、前掲の「静的コード解析の誤検知と限界」で説明しています。
主な商用の静的解析ツールとメーカー
商用ツールは買収や改称で社名が変わっており、古い資料の表記では窓口にたどり着けないことがあります。2026年10月時点の提供元で並べると次のとおりです。
| 製品 | 提供元 | 主な用途 |
|---|---|---|
| Coverity | Black Duck Software | 多言語・大規模コード |
| Perforce QAC | Perforce | C/C++のコーディング規約 |
| Perforce Klocwork | Perforce | 大規模コードの高速解析 |
| Polyspace Bug Finder / Code Prover | MathWorks | 組込み・実行時エラーの証明 |
| CodeSonar | AdaCore | C/C++・バイナリ解析 |
| Parasoft C/C++test | Parasoft | 静的解析と単体テスト |
Perforceは2製品の違いを、QACは解析の深さ、Klocworkは速度と規模を優先する製品として比較ページで説明しており、旧称のHelix QACは現在Perforce QACです。CodeSonarは旧GrammaTechの製品部門だったCodeSecureの製品で、AdaCoreのページには「CodeSonar is now offered as part of AdaCore’s static analysis portfolio」とあります。Parasoft C/C++testの国内総販売代理店はテクマトリックスです。
Coverityの静的解析:提供元・仕組み・Coverity Scan
Coverityは、Synopsysのソフトウェアインテグリティ部門が独立したBlack Duck Softwareの製品です。Black Duckの製品ページは「22 programming languages」と250以上のフレームワーク、IaCへの対応をうたっており、とくにC/C++の安全性重視の組込み開発を強みに挙げています。
解析の流れは、ビルドを捕捉して解析し、結果をサーバーへ送る3段階です。Coverity 2026.3のドキュメントでは、cov-build と cov-analyze で指摘を出し、cov-commit-defects でCoverity Connect(結果を管理するWebサーバー)へ送る手順になっています。cov-build は普段のmakeやmsbuildをCoverity経由で実行してコンパイラの呼び出しを記録するため、このビルド捕捉方式にはビルドできる環境が要ります。ただしCoverity CLIにはビルドコマンドを使わない方式もあり、必要な環境は対象言語と解析方式によって異なります。
価格は公開されていません。国内販売代理店の日立ソリューションズは、利用規模(開発者数など)で決まる年間サブスクリプションで、言語や機能による差は無く、30日間の無償トライアルがあると製品ページで案内しています。
オープンソースのプロジェクトなら、無償のCoverity Scanが使えます。Coverity ScanのFAQは、登録したオープンソース開発者に解析結果を「at no charge」で提供すると説明し、対象をCoverity Scanに登録したオープンソースプロジェクトに限っています。社内の非公開コードの評価には使えないので、商用利用の検討はトライアルで行います。
機能安全・コーディング規約への対応と選定条件
自動車・産業機器・医療機器などで規格適合が求められるC/C++開発では、必要な規格と版への対応を選定条件に入れたうえで、自社コードでの検出結果も比較します。MISRA Cは2025年3月にMISRA C:2025が発行されており、ツールが対応する版を確認しないと、規約の版と検査の版がずれます。ISO 26262-8はソフトウェアツールを使うことへの信頼性(confidence in the use of software tools)を要求事項として扱うため、認証機関のレポートなど、ツール側が出せる証跡も比較対象に入ります。規格対応が不要な場合は、検出範囲、指摘の管理機能、サポートを含む運用費用で無料ツールと比較します。
無料で使える静的解析ツールの言語別一覧
まずは処理系に同梱された解析器か、OSSのツールから始めるのが現実的です。2026年10月時点の最新版で並べます。
| 言語 | ツール | 最新版(2026年10月) |
|---|---|---|
| C/C++ | Clang Static Analyzer・clang-tidy | LLVM 23.1.4 |
| C/C++ | Cppcheck | 2.22.0 |
| Java | SpotBugs | 4.10.5 |
| JavaScript/TypeScript | ESLint | 10.13.0 |
| Python | Ruff | 0.17.1 |
| Go | go vet・Staticcheck | Staticcheck 2026.2.2 |
| C#/Visual Basic | Roslynアナライザー | .NET SDKに同梱 |
| PHP | PHPStan | 2系 |
| 多言語 | Semgrep CE・CodeQL | 条件付きで無料 |
版は各プロジェクトのGitHubのリリースで確認したものです。Cppcheckのリポジトリは cppcheck-opensource/cppcheck に移っており、旧URL(danmar/cppcheck)は転送されます。C#の場合、Roslynアナライザーは.NET SDKに含まれ、.NET 5以降を対象とするプロジェクトでは既定で有効です。PHPの型の検査はPHPStanとは?レベル0〜10の違いと導入・ベースライン運用【2.3対応】で、レベル別に解説しています。
CodeQL・Semgrepの無料利用条件とライセンス
多言語のツールは、無料で使える範囲に条件があります。GitHubのCodeQLによるCode Scanningは公開リポジトリなら無料ですが、非公開リポジトリでは組織所有のリポジトリでGitHub Code Securityを有効にする必要があり、料金はアクティブコミッター1人あたり月30米ドルです。Semgrep Community Editionのエンジン自体はLGPL 2.1ですが、Semgrepが配布するルールはSemgrep Rules Licenseで、利用は自社の内部業務目的に限られます。設定の手順はGitHub Code Scanningとは?CodeQLの設定手順・対応言語・料金【2026年9月】とSemgrepとは?使い方・ルールの書き方・料金と無料版の検出範囲【v1.180で実測】にまとめています。
レポートやダッシュボードを含めて多言語をまとめて管理したい場合はSonarQubeが候補になります。エディションごとの違いはSonarQube(ソナーキューブ)とは?できること・料金・使い方と品質ゲートの設定【2026年10月】で解説しています。
静的解析ツールの選び方:判断の順番
ツールを並べて機能を比べる前に、次の順で絞ると判断がぶれません。
- コンパイラの推奨警告設定を有効にし、処理系に同梱の解析器(clangの解析モード、go vet、Roslynアナライザー)をかける
- CIで止める条件を決め、終了コードで落ちる設定にする
- C/C++なら性格の違う解析器(cppcheckなど)をもう1本足し、取りこぼしを比べる
- 自社コードで出た指摘の件数と中身を見てから、商用ツールのトライアルにかける
商用ツールを最初に買うのは、MISRA CやISO 26262のように規格への適合を示す必要がある場合と、大規模なC/C++を関数やファイルをまたいで解析したい場合などです。ただし、多言語の脆弱性検出や組織全体の管理機能が要件なら、規模にかかわらず最初から商用も比較対象に入れます。それ以外で先に商用を入れると、指摘の多さに対して読む体制が追いつかず、既存の指摘が放置されたまま費用だけが続く結果になりがちです。比較表のどの項目よりも、手元のコードで何件出るかのほうが判断の材料になります。
静的解析は、コード品質を測る仕組みの一部です。カバレッジや複雑度と合わせてCIの品質ゲートに組み込む方法はコード品質とは?測る指標と品質ゲートでCIに組み込む手順で扱っています。
よくある質問
静的解析ツールとコンパイラの警告機能は何が違いますか?
コンパイラの警告も静的解析の一種で、制御フローやデータフローを調べるものがあります。専用の解析モードとは検査項目や解析範囲が異なります。この記事の例では、-Wall -Wextra で何も出ないヌル参照とメモリリークを、同じclangの解析モードが経路つきで指摘しました。逆に配列の範囲外読み取りは警告だけが拾ったので、どちらか一方で済ませず両方を有効にします。
コベリティ(Coverity)の静的解析とは何ですか?
Black Duck Softwareが提供する商用の静的解析ツールです。ビルドを捕捉する cov-build、解析する cov-analyze、結果をCoverity Connectへ送る cov-commit-defects の流れで使い、22言語に対応します。オープンソースのプロジェクトは、登録すれば無償のCoverity Scanで解析できます。
静的解析でバグを早期に検出できるツールのメーカーはどこですか?
商用ではBlack Duck Software(Coverity)、Perforce(Perforce QAC・Perforce Klocwork)、MathWorks(Polyspace)、AdaCore(CodeSonar)、Parasoft(C/C++test)が代表的です。OSSではLLVMプロジェクトのClang Static AnalyzerやCppcheckが無料で使えます。
無料の静的解析ツールだけで足りますか?
機能安全規格や監査用レポートの要否だけでは判断できません。まずClang Static AnalyzerやCppcheckを自社コードに適用し、必要な検査項目、対応言語、運用機能を満たすか確認します。この記事の比較のように無料のツール同士でも検出が違うため、2本を併用し、それでも取りこぼしが問題になるかを見てから商用を検討してください。
静的解析と動的解析の違いは何ですか?
プログラムを実行するかどうかが分かれ目です。静的解析は実行せずにコードを検査し、動的解析は実際に動かして挙動を観測します。静的解析でも入力条件に依存する不具合を検出できますが、解析対象やモデル化には限界があるため、テストやAddressSanitizerのような動的な手段と組み合わせます。