编辑
2023-11-28
Misc
00
请注意,本文编写于 427 天前,最后修改于 426 天前,其中某些信息可能已经过时。

目录

Clang-tidy
Clang-tidy 与 Static Analyzer的对比
Clang-tidy开发流程
使用手册
开发实例

😅

Clang-tidy


Clang-tidy 与 Static Analyzer的对比

Clang-tidyStatic Analyzer
使用语法树匹配使用符号执行
使用了AST使用了AST

Clang-tidy开发流程

使用手册

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是基于语法树进行检查的 所以第一步就是要搞清对应检查规则的语法树 这里针对测试用例 检查是否存在memcpysize字段过大的问题

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 许可协议。转载请注明出处!