ClangのUndefinedAssignmentCheckerを試してみる

ClangのUndefinedAssignmentCheckerを試してみます。

 

1. Checkerの実行方法

$ clang -cc1 -analyzer-checker=core.uninitialized.Assign ¥
-analyze <target>.c

2. 機能

UndefinedAssignmentCheckerのヘッダ部分のコメントによれば、未定義の値を 代入していることを確認するCheckerのようです。

3. UndefinedAssignmentCheckerの実装

checkBindで代入式を補足しています。isUndefで未定義の値を判定しています。

void UndefinedAssignmentChecker::checkBind(SVal location, SVal val,
                                           const Stmt *StoreE,
                                           CheckerContext &C) const {
  if (!val.isUndef())
    return;
<snip>
}

3.1. isUndef

SValのgetRawKind()がUndefinedKindの場合にisUndefがtrueになります。

inline bool isUndef() const {
  return getRawKind() == UndefinedKind;
}

4. checkBindとcheckLocationの違い

checkBindのコメントに"Called on binding of a value to a location" と記載されております。
valueとlocationの違いが難しい。valueは1等の変数定義を持たない値に対し、 locationはint a等の変数定義を持つものと私は理解しております。 checkBindではvalueとlocationを扱うのに対し、checkLocationではlocation しか扱わないようです。

4.1. checkBindとcheckLocationを補足するChecker

以下のようなCheckerを使用して、checkBindとcheckLocationに渡されるSVal のisUndefがどうなるかを出力してみます。

void BindChecker::checkBind(SVal Loc, SVal Val, const Stmt *S,
                            CheckerContext &C) const {
  debug_stmt(S, C.getSourceManager());
  llvm::errs() << "Loc.isUndef() = " << Loc.isUndef() << "\n";
  llvm::errs() << "Val.isUndef() = " << Val.isUndef() << "\n";
}
 
void LocationChecker::checkLocation(SVal Loc, bool IsLoad, const Stmt *S,
                                                                        CheckerContext &C) const {
  debug_stmt(S, C.getSourceManager());
  llvm::errs() << "Loc.isUndef() = " << Loc.isUndef() << "\n";
}

4.2. 対象コード

初期化されていないrhsをlhsに代入します。

$ cat uninit.c 
#include <stdio.h>
 
int main(void)
{
  int lhs, rhs;
  lhs = rhs;
  return 0;
}

4.3. 実行結果

checkBindの実行結果は以下の通りです。BinaryOperatorを補足していますね。 int a = 1という初期化処理のBinaryOperatorも補足するようです。 左辺のlocationに右辺のvalueを結びつけているということですね。 Loc.isUndef()はfalseになるのに対し、Val.isUndef()はtrueとなっておりま す。lhsという変数自体は未定義ではなく、rhsの中身が未定義ということだと 思います。

BinaryOperator
  lhs = rhs;
  ~~~~~~~
Loc.isUndef() = 0
Val.isUndef() = 1

checkLocationの実行結果は以下の通りです。BinaryOperatorの右辺と左辺を 補足しています。int a = 1という初期化処理は補足しないようです。 こちらはあくまでlocationに特化してます。isLoadの値でStoreなのかLoadな のかを判別できます。
Loc.isUndef()はfalseとなります。

$ ./LocationChecker.sh uninit.c 
ImplicitCastExpr
  lhs = rhs;
        ~
Loc.isUndef() = 0
DeclRefExpr
  lhs = rhs;
  ~
Loc.isUndef() = 0

まとめると、未初期化の変数を補足するにはvalueを補足できるcheckBindでの み可能ということのようです。

4. UndefinedAssignmentCheckerの実行結果

先ほどのuninit.cに対してUndefinedAssignmentCheckerを実行します。

uninit.c:6:7: warning: Assigned value is garbage or undefined
  lhs = rhs;
      ^ ~~~
1 warning generated.

5. まとめ

checkBindとcheckLocationの違いはcheckBindがlocationとvalueを補足するの に対し、checkLocationはlocationのみ補足するところです。 未定義の値の代入と聞くと、未定義の関数や変数を代入しているととらえられ ますが、そもそもCheckerが動く前にコンパイルエラーになる為、本Checkerは あくまで初期化していない変数の代入のwarningに留まるようです。

ダウンロード
ソースコード
対象コードとCheckerのコード
src.tar.gz
GNU tar 991 Bytes