😅
Clang-tidy | Static Analyzer |
---|---|
使用语法树匹配 | 使用符号执行 |
使用了AST | 使用了AST |
bash$ bear make # 针对Makefile项目生成compile_command.json
$ cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -G Ninja ../ # 针对Cmake项目生成compile_command.json
$ /home/du4t/Desktop/Project/llvm-project/clang-tools-extra/clang-tidy/tool/run-clang-tidy.py
开发手册: https://clang.llvm.org/docs/LibASTMatchersReference.html
首先使用脚手架快速创建相关文件 此时相关文件存在于/home/du4t/Desktop/Project/llvm-project/clang-tools-extra/clang-tidy/misc
中
bash$ cd /home/du4t/Desktop/Project/llvm-project/clang-tools-extra/clang-tidy
$ ./add_new_check.py [module] [name]
由于Clang-tidy是基于语法树进行检查的 所以第一步就是要搞清对应检查规则的语法树 这里针对测试用例 检查是否存在memcpy
的size
字段过大的问题
c#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
int main(int argc, char** argv)
{
int len = 0x10000;
void *p = malloc(0x100);
void *p1 = malloc(0x100);
memcpy(p, p1, len);
while(1){
len--;
}
}
使用Clang生成语法树
c$ clang -Xclang -ast-dump -fsyntax-only test.c
bash|-FunctionDecl 0x55d91ce19350 <test.c:5:1, line:14:1> line:5:5 main 'int (int, char **)'
| |-ParmVarDecl 0x55d91ce191f8 <col:10, col:14> col:14 argc 'int'
| |-ParmVarDecl 0x55d91ce19278 <col:20, col:27> col:27 argv 'char **'
| `-CompoundStmt 0x55d91ce1a050 <line:6:1, line:14:1>
| |-DeclStmt 0x55d91ce194b8 <line:7:5, col:26>
| | `-VarDecl 0x55d91ce19418 <col:5, col:16> col:9 used len 'int' cinit
| | `-UnaryOperator 0x55d91ce194a0 <col:15, col:16> 'int' prefix '-'
| | `-IntegerLiteral 0x55d91ce19480 <col:16> 'int' 2147483647
| |-DeclStmt 0x55d91ce19610 <line:8:5, col:28>
| | `-VarDecl 0x55d91ce194e8 <col:5, col:27> col:11 used p 'void *' cinit
| | `-CallExpr 0x55d91ce195d0 <col:15, col:27> 'void *'
| | |-ImplicitCastExpr 0x55d91ce195b8 <col:15> 'void *(*)(unsigned long)' <FunctionToPointerDecay>
| | | `-DeclRefExpr 0x55d91ce19550 <col:15> 'void *(unsigned long)' Function 0x55d91cda4c70 'malloc' 'void *(unsigned long)'
| | `-ImplicitCastExpr 0x55d91ce195f8 <col:22> 'unsigned long' <IntegralCast>
| | `-IntegerLiteral 0x55d91ce19570 <col:22> 'int' 256
| |-DeclStmt 0x55d91ce19740 <line:9:5, col:29>
| | `-VarDecl 0x55d91ce19640 <col:5, col:28> col:11 used p1 'void *' cinit
| | `-CallExpr 0x55d91ce19700 <col:16, col:28> 'void *'
| | |-ImplicitCastExpr 0x55d91ce196e8 <col:16> 'void *(*)(unsigned long)' <FunctionToPointerDecay>
| | | `-DeclRefExpr 0x55d91ce196a8 <col:16> 'void *(unsigned long)' Function 0x55d91cda4c70 'malloc' 'void *(unsigned long)'
| | `-ImplicitCastExpr 0x55d91ce19728 <col:23> 'unsigned long' <IntegralCast>
| | `-IntegerLiteral 0x55d91ce196c8 <col:23> 'int' 256
| |-CallExpr 0x55d91ce19f10 <line:10:5, col:22> 'void *'
| | |-ImplicitCastExpr 0x55d91ce19ef8 <col:5> 'void *(*)(void *, const void *, unsigned long)' <FunctionToPointerDecay>
| | | `-DeclRefExpr 0x55d91ce19e08 <col:5> 'void *(void *, const void *, unsigned long)' Function 0x55d91ce19bc0 'memcpy' 'void *(void *, const void *, unsigned long)'
| | |-ImplicitCastExpr 0x55d91ce19f48 <col:12> 'void *' <LValueToRValue>
| | | `-DeclRefExpr 0x55d91ce19e28 <col:12> 'void *' lvalue Var 0x55d91ce194e8 'p' 'void *'
| | |-ImplicitCastExpr 0x55d91ce19f78 <col:15> 'const void *' <NoOp>
| | | `-ImplicitCastExpr 0x55d91ce19f60 <col:15> 'void *' <LValueToRValue>
| | | `-DeclRefExpr 0x55d91ce19e48 <col:15> 'void *' lvalue Var 0x55d91ce19640 'p1' 'void *'
| | `-ImplicitCastExpr 0x55d91ce19fa8 <col:19> 'unsigned long' <IntegralCast>
| | `-ImplicitCastExpr 0x55d91ce19f90 <col:19> 'int' <LValueToRValue>
| | `-DeclRefExpr 0x55d91ce19e68 <col:19> 'int' lvalue Var 0x55d91ce19418 'len' 'int'
| `-WhileStmt 0x55d91ce1a030 <line:11:5, line:13:5>
| |-IntegerLiteral 0x55d91ce19fc0 <line:11:11> 'int' 1
| `-CompoundStmt 0x55d91ce1a018 <col:13, line:13:5>
| `-UnaryOperator 0x55d91ce1a000 <line:12:6, col:9> 'int' postfix '--'
| `-DeclRefExpr 0x55d91ce19fe0 <col:6> 'int' lvalue Var 0x55d91ce19418 'len' 'int'
由此得出语法树检查规则
c//===--- My_testCheck.cpp - clang-tidy ------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "My_testCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
namespace misc {
void My_testCheck::registerMatchers(MatchFinder *Finder) {
// FIXME: Add matchers.
Finder->addMatcher(callExpr(callee(functionDecl(hasName("memcpy"))), hasArgument(2, declRefExpr().bind("thirdArg"))).bind("memcpy"), this);
}
void My_testCheck::check(const MatchFinder::MatchResult &Result) {
// FIXME: Add callback implementation.
const auto *memcpyCall = Result.Nodes.getNodeAs<CallExpr>("memcpy");
const auto *thirdArg = Result.Nodes.getNodeAs<DeclRefExpr>("thirdArg");
if (memcpyCall && thirdArg)
{
const VarDecl *Var = dyn_cast<VarDecl>(thirdArg->getFoundDecl());
if (Var && Var->hasInit() && isa<IntegerLiteral>(Var->getInit()))
{
const auto *InitValue = cast<IntegerLiteral>(Var->getInit());
if (InitValue->getValue().getZExtValue() > 0x1000)
{
SourceManager &SM = *Result.SourceManager;
SourceLocation loc = memcpyCall->getSourceRange().getBegin();
if (loc.isValid())
{
diag(loc, "memcpy call has third argument larger than 0x1000")<<loc;
}
}
}
}
}
} // namespace misc
} // namespace tidy
} // namespace clang
检查结果如下
bash$ /home/du4t/Desktop/Project/llvm-project/build_release/bin/clang-tidy --checks="-*, misc-My_test" ./test.c
bash/home/du4t/Desktop/Test/test.c:10:5: warning: memcpy call has third argument larger than 0x1000 [misc-My_test] memcpy(p, p1, len); ^~~~~~ Suppressed 1 warnings (1 with check filters).
但是由于是静态检查 只能说检查结果差强人意 下面这个样例就是未检出 还是不如Static Analyzer
但是好处是Clang-tidy
是可以兼容Static Analyzer
的
c#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
int main(int argc, char** argv)
{
int len = 0x1;
void *p = malloc(0x100);
void *p1 = malloc(0x100);
len += 0x1000;
memcpy(p, p1, len);
while(1){
len--;
}
}
如果是需要扫描一个项目的话 需要针对项目编译的compile_command.json
此处就体现出另一个弊端 Clang-tidy
无法输出一个完整的报告 而Static Analyzer
可以自己生成报告
bash$ bear make # 针对Makefile项目生成compile_command.json
$ cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -G Ninja ../ # 针对Cmake项目生成compile_command.json
$ /home/du4t/Desktop/Project/llvm-project/clang-tools-extra/clang-tidy/tool/run-clang-tidy.py
本文作者:Du4t
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!