使用 ASTMatcher 获取所有 "malloc" 个调用

Get all "malloc" calls using ASTMatcher

我试图在 clang 中使用 ASTMatcher 获取所有 malloc 调用。这是代码示例:

Finder.addMatcher(
      callExpr(
        hasParent(binaryOperator(
          hasOperatorName("=")).bind("assignment")),
          declRefExpr(to(functionDecl(hasName("malloc"))))).bind("functionCall"),
      &HandlerForFunctionCall);

编译正常。但我仍然无法接到任何 malloc 电话。如何使用 clang ASTMatcher 获取所有 malloc 调用?

问题

malloc函数的签名定义如下:

void* malloc (size_t size);

为了将 malloc 的 return 值分配给 void* 以外的任何类型的指针,您必须对其进行强制转换。 虽然 C++ 要求您显式转换,但 C 编译器会隐式为您执行此操作。所以即使你写

int *a = malloc(sizeof(*a));

编译器将隐式转换 RHS 表达式。那相当于

int *a = (int*) malloc(sizeof(*a));

您正在使用 hasParent 缩小匹配器,它只匹配 直接 parents 而不是任何祖先 . 因此,您的匹配器将只匹​​配没有任何类型转换的分配。

您的 declRefExpr 也发生了几乎相同的事情。 C 标准说函数会自动衰减到 pointers-to-functions。 Clang 隐式地将 malloc 转换为 void *(*)(size_t),这会破坏匹配器的层次结构。

可能的解决方案

好吧,这取决于您实际想要做什么。 首先,您通常可以使用以下代码段修复部分 selecting malloc 函数:

callExpr(callee(functionDecl(hasName("malloc"))))

剩下的就看你想要什么了select。 如果您只对匹配上面第一个示例中的直接赋值感兴趣,那么您可以使用 ignoringImpCasts 匹配器。 出于某种原因,我无法像您编写的那样将它插入匹配器中,所以只需反转匹配器即可。 看起来像这样:

binaryOperator(
  hasOperatorName("="),
  hasRHS(ignoringImpCasts(
    callExpr(
      callee(functionDecl(hasName("malloc")))
    ).bind("functionCall")
  ))
).bind("assignment")

如果您还想像第二个示例那样包含显式强制转换,请改用 ignoringParenImpCasts

binaryOperator(
  hasOperatorName("="),
  hasRHS(ignoringParenImpCasts(
    callExpr(
      callee(functionDecl(hasName("malloc")))
    ).bind("functionCall")
  ))
).bind("assignment")

如果您对包含 malloc 的任意表达式的所有赋值感兴趣,请改用 hasAncestor。它不仅直接匹配parents,而且向上遍历直到匹配你的节点:

callExpr(
  callee(functionDecl(hasName("malloc"))),
  hasAncestor(
    binaryOperator(hasOperatorName("=")).bind("assignment")
  )
).bind("functionCall")

还有一件事。 您可能只对直接匹配源代码中定义的内容感兴趣,而不是包含 header 文件中的内容。 只需将 unless(isExpansionInSystemHeader()) 添加到您的 top-level 匹配器,它将从系统 headers.

中排除所有定义

请注意,此代码已使用 LLVM 3.7 进行测试,未来的更改可能会破坏它。

如何调试

好吧,那我们到底是怎么知道的呢? 事实证明,Clang 已经为您提供了您所需要的一切:) 具体来说,有两个功能您可能会感兴趣。

当您使用 -Xclang ast-dump -fsyntax-only 调用 Clang 时,它将打印出翻译单元的漂亮多彩的 AST。不要惊讶地发现包含来自系统 header 的所有声明的巨大序言,因为它必须先 运行 预处理器才能生成 AST。示例:

$ clang -Xclang -ast-dump -fsyntax-only example.c
...

`-FunctionDecl 0x3f2fc28 <line:19:1, line:31:1> line:19:5 main 'int ()'
  `-CompoundStmt 0x3f307b8 <line:20:1, line:31:1>
    |-BinaryOperator 0x3f2ff38 <line:22:3, col:29> 'int *' '='
    | |-DeclRefExpr 0x3f2fd40 <col:3> 'int *' lvalue Var 0x3f2f388 'a' 'int *'
    | `-ImplicitCastExpr 0x3f2ff20 <col:7, col:29> 'int *' <BitCast>
    |   `-CallExpr 0x3f2fef0 <col:7, col:29> 'void *'
    |     |-ImplicitCastExpr 0x3f2fed8 <col:7> 'void *(*)(unsigned long)' <FunctionToPointerDecay>
    |     | `-DeclRefExpr 0x3f2fd68 <col:7> 'void *(unsigned long)' Function 0x3f1cdd0 'malloc' 'void *(unsigned long)'
    |     `-BinaryOperator 0x3f2fe88 <col:15, col:28> 'unsigned long' '*'
    |       |-ImplicitCastExpr 0x3f2fe70 <col:15> 'unsigned long' <IntegralCast>
    |       | `-ImplicitCastExpr 0x3f2fe58 <col:15> 'int' <LValueToRValue>
    |       |   `-DeclRefExpr 0x3f2fd90 <col:15> 'int' lvalue Var 0x3f2f488 'n' 'int'
    |       `-UnaryExprOrTypeTraitExpr 0x3f2fe38 <col:19, col:28> 'unsigned long' sizeof
    |         `-ParenExpr 0x3f2fe18 <col:25, col:28> 'int' lvalue
    |           `-UnaryOperator 0x3f2fdf8 <col:26, col:27> 'int' lvalue prefix '*'
    |             `-ImplicitCastExpr 0x3f2fde0 <col:27> 'int *' <LValueToRValue>
    |               `-DeclRefExpr 0x3f2fdb8 <col:27> 'int *' lvalue Var 0x3f2f388 'a' 'int *'

...

然后是 clang-query 如果您从源代码编译它,它会与 clang 一起构建。 它是 libTooling 的一个很好的例子,同时对开发有绝对惊人的帮助。 您只需在示例源文件上 运行 它并使用它来测试您的匹配器(请注意,它隐式地将 "root" 绑定到完整的匹配器):

$ <llvm>/bin/clang-query example.c --
clang-query> match callExpr(callee(functionDecl(hasName("malloc"))),hasAncestor(binaryOperator(hasOperatorName("=")).bind("assignment"))).bind("functionCall")

Match #1:

/vagrant/tests/true-valid-memsafety.c:22:3: note: "assignment" binds here
  a = malloc (n * sizeof(*a));
  ^~~~~~~~~~~~~~~~~~~~~~~~~~~
/vagrant/tests/true-valid-memsafety.c:22:7: note: "functionCall" binds here
  a = malloc (n * sizeof(*a));
      ^~~~~~~~~~~~~~~~~~~~~~~
/vagrant/tests/true-valid-memsafety.c:22:7: note: "root" binds here
  a = malloc (n * sizeof(*a));
      ^~~~~~~~~~~~~~~~~~~~~~~

Match #2:

/vagrant/tests/true-valid-memsafety.c:23:3: note: "assignment" binds here
  b = malloc (n * sizeof(*b));
  ^~~~~~~~~~~~~~~~~~~~~~~~~~~
/vagrant/tests/true-valid-memsafety.c:23:7: note: "functionCall" binds here
  b = malloc (n * sizeof(*b));
      ^~~~~~~~~~~~~~~~~~~~~~~
/vagrant/tests/true-valid-memsafety.c:23:7: note: "root" binds here
  b = malloc (n * sizeof(*b));
      ^~~~~~~~~~~~~~~~~~~~~~~
2 matches.

如果您对有关该主题的更多信息感兴趣,请转到 this excellent blog post by Eli Bendersky for a good overview and introduction. The complete documentation for AST matchers can be found here