CheckSizeofPointerを試してみます。
1. 実行方法
clang -cc1 -analyzer-checker=alpha.core.SizeofPtr \
-analyze $@
2. CWE-467
CheckSizeofPointer.cppのヘッダ部分によれば、sizeofにポインタを使用して いるコードを検出するCheckerだそうです。 コメントによれば、脆弱性カタログのCWE-467を見つけるものだそうです。 CWE-467はmalloc領域を指すポインタにsizeofを使った場合の問題を指摘する ものです。
3. CheckSizeofPointerの実装
StmtVisitorを継承し、VisitStmtとVisitUnaryExprOrTypeTraitExprを実装し ています。VisitStmtでVisitChildrenを呼び出し、VisitChildrenで child_iteratorを辿ります。よって、UnaryExprOrTypeTraitExprを見つけるま ではchild_iteratorで再帰的にASTを辿るようになっています。
class WalkAST : public StmtVisitor<WalkAST> {
<snip>
void VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *E);
void VisitStmt(Stmt *S) { VisitChildren(S); }
void VisitChildren(Stmt *S);
};
3.1. UnaryExprOrTypeTraitExpr
sizeof(x)はUnaryExprOrTypeTraitExprで補足されます。sizeof以外にも alignofとvec_stepも補足されるようです。
3.2. VisitUnaryExprOrTypeTraitExpr
getKindで取得できる値はUETT_SizeOf, UETT_AlignOf, UETT_VecStepです。こ こではsizeofを補足するのでUETT_SizeOfでないものはフィルタリングします。
void WalkAST::VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *E) {
if (E->getKind() != UETT_SizeOf)
return;
このisArgumentTypeが分からない・・・。いつtrueになるのだろうか。 explicit typeもキャストに関する検索はひっかかるが、sizeofに使うという 話を見つけられてません。
// If an explicit type is used in the code, usually the coder knows what he is
// doing.
if (E->isArgumentType())
return;
sizeof(x)のxの型を取り出します。型がポインタかどうかを確認します。
QualType T = E->getTypeOfArgument();
if (T->isPointerType()) {
char **qをsizeof(*q)は!isa<DeclRefExpr>(ArgEx->IgnoreParens())がtrueに なります。char *pのsizeof(p)しか補足しないようになっていますね。
// Many false positives have the form 'sizeof *p'. This is reasonable
// because people know what they are doing when they intentionally
// dereference the pointer.
Expr *ArgEx = E->getArgumentExpr();
if (!isa<DeclRefExpr>(ArgEx->IgnoreParens()))
return;
<snip> # warning出力
}
}
char **qのsizeof(*q)をダンプするとUnaryExprOrTypeTraitExprが入れ子になっ ていることを確認できます。
UnaryExprOrTypeTraitExpr 0x7f9d9b8b7710 'unsigned long' sizeof
`-ParenExpr 0x7f9d9b8b76f0 'char *' lvalue
`-UnaryOperator 0x7f9d9b8b76d0 'char *' lvalue prefix '*'
`-ImplicitCastExpr 0x7f9d9b8b76b8 'char **' <LValueToRValue>
`-DeclRefExpr 0x7f9d9b8b7690 'char **' lvalue Var 0x7f9d9b8b6d80 'q' 'char **'
これはchar array[5]でsizeof(array)/sizeof(*array)で要素数を割り出す方 法で、sizeof(array)をwarning出力しないようにする為です。
4. CheckSizeofPointerの実行結果
4.1. 対象コード
#include <stdio.h>
int main(void)
{
long l;
int i;
char *p, array[64];
printf("sizeof(l) = %u\n", (unsigned) sizeof(l));
printf("sizeof(i) = %u\n", (unsigned) sizeof(i));
printf("sizeof(*p) = %u\n", (unsigned) sizeof(*p));
printf("sizeof(p) = %u\n", (unsigned) sizeof(p));
printf("sizeof(array) / sizeof(*array) = %u\n",
(unsigned) (sizeof(array) / sizeof(*array)));
return 0;
}
私の環境で本プログラムを実行すると以下のようになりました。
sizeof(l) = 8 sizeof(i) = 4 sizeof(*p) = 1 sizeof(p) = 8 sizeof(array) / sizeof(*array) = 64
4.2. 実行結果
sizeof.c:12:41: warning: The code calls sizeof() on a pointer
type. This can produce an unexpected result
printf("sizeof(p) = %u\n", (unsigned) sizeof(p));
^ ~~~
1 warning generated.
5. まとめ
UnaryExprOrTypeTraitExprでsizeofを補足できることが分かりました。 CheckSizeofPointerでsizeofにポインタを使用しているコードを検出できます。 配列の要素数を取得するsizeof(array)/sizeof(*array)などのコードは検出対 象から外れるようになっています。