静的解析とは
皆さんはコンパイルの際に、Warning(警告)メッセージが出力されたらどうしていますか?
WarningはErrorと異なり、残っていたとしてもバイナリが出力されますし、必ずしも不具合に直結するとは限らないため、放置しているケースもあるかと思います。趣味のプログラミングならそれでも良いかもしれませんが、内容によっては致命的な不具合につながる場合もあることから、開発現場では原則Warningは除去することが求められます。
個人的にはWarningが残っているソースコードを他人に見られるのは恥ずかしいということもありますね。
コンパイル時のWarning指摘のように、プロフラムを実行せずにソースコードを解析してプログラムの検証を行うことを静的解析といいます。
コンパイルの際のWarningメッセージはプリプロセッサやコンパイラが構文解析をする際に致命的ではないけど暗黙的だったり冗長だったりといった内容について警告という形で指摘をしてくれるものですが、静的解析ツールはより詳細にソースコードを解析して「おかしなコード」を見つけてくれるものです。代表的な「おかしな(おかしいかもしれない)コード」には以下のようなものがあります。
- 型が一致しない比較や代入
- 宣言されているのに使用されない変数や関数
- 代入・初期化せずに参照される変数
- 戻り値がある関数を使用しているが戻り値を参照していない
プログラミングの工程で不具合を検出することで開発のコストダウンにもつながることから、昨今は組込みの開発現場でも静的解析のエビデンスを厳密に管理されるプロジェクトが多くなっている印象です。
静的解析ツールとしては昔ながらの「lint」が有名ですが、開発現場では様々な有償市販ツールが導入されています。そこにお金をかけたとしてもプロジェクト全体ではコストダウンにつながると判断されているということです。また、最近ではIoTの流れもあってセキュアコーディング[1]プログラミングにおいて脆弱性を排除するという考え方で、静的解析が大きな位置を占めます。についての重要性が高まっているということも大きいようです。
Splintを使ってみる
気軽に使用できる静的解析ツールの代表格として「Splint」があります。前述したlintの発展形ツールです。
以下、Ubuntu上でSplintを実行する例を示します。
Splintのインストール
Splintをインストールします。
$ sudo apt install splint
実行例
lintのWikipediaで紹介されている「lintで警告が出る例」のソースコードを基に作成したサンプルコードで実行してみます。
#include <stdio.h>
int foo(int count) {
int sum = 0;
int i;
for (i = 1; i <= count; ++i) {
sum += i;
}
if (sum >= 100) {
return sum;
}
}
int main()
{
int ret;
ret = foo(1);
printf("ret=%d\n", ret);
return 0;
}
Splintの基本的な実行手順は以下の通りです。
$ splint +standard example.c
以下のような警告が出力されます。
Splint 3.1.2 --- 20 Feb 2018 example.c: (in function foo) example.c:12:2: Path with no return in function declared to return int There is a path through a function declared to return a value on which there is no return statement. This means the execution may fall through without returning a meaningful result to the caller. (Use -noret to inhibit warning) Finished checking --- 1 code warning
【警告内容の意訳】
intを返すように宣言された関数にreturnがないパスがある。これは、呼び出し元に意味のある結果を返さずに実行が失敗する可能性があることを意味する。
Wikipediaにも記載がありますが、最近のコンパイラは警告やエラーを検出する機能が強化されており、例えばgccであれば「-Wreturn-type」のオプションを指定することで本例のコードに対して警告を出力します。
$ gcc -Wreturn-type example.c example.c: In function ‘foo’: example.c:12:1: warning: control reaches end of non-void function [-Wreturn-type] 12 | } | ^
注釈
↑1 | プログラミングにおいて脆弱性を排除するという考え方で、静的解析が大きな位置を占めます。 |
---|
コメント