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)などのコードは検出対 象から外れるようになっています。