clang - Get all "malloc" calls using ASTMatcher -
i'm trying malloc calls using astmatcher in clang. code sample:
finder.addmatcher( callexpr( hasparent(binaryoperator( hasoperatorname("=")).bind("assignment")), declrefexpr(to(functiondecl(hasname("malloc"))))).bind("functioncall"), &handlerforfunctioncall); it compiles fine. still couldn't malloc calls. how can malloc calls using clang astmatcher?
the problem
the signature of malloc function defined follows:
void* malloc (size_t size); in order assign return value of malloc pointer of type other void*, have cast it. while c++ requires cast explicitly, c compiler implicitly you. if write
int *a = malloc(sizeof(*a)); the compiler implicitly cast rhs expression. equivalent to
int *a = (int*) malloc(sizeof(*a)); you using hasparent narrowing matcher, which matches direct parents , not ancestors. therefore matcher match assignments without type of cast.
and pretty same thing happens declrefexpr. c standard says functions automatically decay pointers-to-functions automatically. clang implicitly casts malloc void *(*)(size_t) , breaks hierarchy of matchers.
possible solutions
well, depends on want do. first of all, can fix part selecting malloc functions using snippet:
callexpr(callee(functiondecl(hasname("malloc")))) the rest depends on want select. if interested in matching direct assignments in first example above, can use ignoringimpcasts matcher. reason have not been able insert in matcher wrote it, invert matcher. looks this:
binaryoperator( hasoperatorname("="), hasrhs(ignoringimpcasts( callexpr( callee(functiondecl(hasname("malloc"))) ).bind("functioncall") )) ).bind("assignment") if additionally want include explicit casts in second example, use ignoringparenimpcasts instead:
binaryoperator( hasoperatorname("="), hasrhs(ignoringparenimpcasts( callexpr( callee(functiondecl(hasname("malloc"))) ).bind("functioncall") )) ).bind("assignment") if interested in assignments arbitrary expressions containing malloc, use hasancestor instead. not match direct parents traverses until matches node:
callexpr( callee(functiondecl(hasname("malloc"))), hasancestor( binaryoperator(hasoperatorname("=")).bind("assignment") ) ).bind("functioncall") one more thing. interested in matching whatever defined in source code directly , not in included header files. add unless(isexpansioninsystemheader()) top-level matcher , exclude definitions system headers.
note code has been tested llvm 3.7 , future changes might break it.
how debug
allright, how hell should know that? turns out, clang provides need :) specifically, there 2 features might interested in.
when invoke clang -xclang ast-dump -fsyntax-only print out pretty , colorful ast of translation unit. don't surprised find huge preamble declarations system headers included, has run preprocessor first generate ast. example:
$ 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 *' ... and there clang-query built along clang if compile sources. excellent example of libtooling , absolutely amazing in development @ same time. run on example source file , use test matchers (note implicitly binds "root" complete matcher):
$ <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 = malloc (n * sizeof(*a)); ^~~~~~~~~~~~~~~~~~~~~~~~~~~ /vagrant/tests/true-valid-memsafety.c:22:7: note: "functioncall" binds here = malloc (n * sizeof(*a)); ^~~~~~~~~~~~~~~~~~~~~~~ /vagrant/tests/true-valid-memsafety.c:22:7: note: "root" binds here = 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. if interested in more information on topic, head on this excellent blog post eli bendersky overview , introduction. complete documentation ast matchers can found here.
Comments
Post a Comment