|
| 1 | +/** |
| 2 | + * This module intends to reduce the difficulty of handling the pattern where implementations |
| 3 | + * implement a function as a macro: the class `StdFunctionOrMacro<...>::Call` matches both std |
| 4 | + * function calls as well as std function macro expansions. |
| 5 | + * |
| 6 | + * For instance, `atomic_init` may be implemented as a function, but is also implemented as |
| 7 | + * `#DEFINE atomic_init(x) __c11_atomic_init(x)` on some platforms. This module aids in finding |
| 8 | + * calls to any standard function which may be a macro, and has predefined behavior for |
| 9 | + * handling `__c11_*` macros. |
| 10 | + * |
| 11 | + * Since a macro can be defined to expand to any expression, we cannot know generally which |
| 12 | + * expanded expressions in `f(x, y)` correspond to arguments `x` or `y`. To handle this, the |
| 13 | + * following inference options are available: |
| 14 | + * - `NoMacroExpansionInference`: Assume any expression in the macro expansion could correspond to |
| 15 | + * any macro argument. |
| 16 | + * - `C11FunctionWrapperMacro`: Check if the macro expands to a function call prefixed with |
| 17 | + * `__c11_` and if so, return the corresponding argument. Otherwise, fall back to |
| 18 | + * `NoMacroExpansionInference`. |
| 19 | + * - `InferMacroExpansionArguments`: Implement your own logic for inferring the argument. |
| 20 | + * |
| 21 | + * To use this module, pick one of the above inference strategies, and then create a predicate for |
| 22 | + * the name you wish to match. For instance: |
| 23 | + * |
| 24 | + * ```codeql |
| 25 | + * private string atomicInit() { result = "atomic_init" } |
| 26 | + * |
| 27 | + * from StdFunctionOrMacro<C11FunctionWrapperMacro, atomicInit/0>::Call c |
| 28 | + * select c.getArgument(0) |
| 29 | + * ``` |
| 30 | + */ |
| 31 | + |
| 32 | +import cpp as cpp |
| 33 | + |
| 34 | +/** Specify the name of your function as a predicate */ |
| 35 | +signature string getName(); |
| 36 | + |
| 37 | +/** Signature module to implement custom argument resolution behavior in expanded macros */ |
| 38 | +signature module InferMacroExpansionArguments { |
| 39 | + bindingset[mi, argumentIdx] |
| 40 | + cpp::Expr inferArgument(cpp::MacroInvocation mi, int argumentIdx); |
| 41 | +} |
| 42 | + |
| 43 | +/** Assume all subexpressions of an expanded macro may be the result of any ith argument */ |
| 44 | +module NoMacroExpansionInference implements InferMacroExpansionArguments { |
| 45 | + bindingset[mi, argumentIdx] |
| 46 | + cpp::Expr inferArgument(cpp::MacroInvocation mi, int argumentIdx) { |
| 47 | + result.getParent*() = mi.getExpr() |
| 48 | + } |
| 49 | +} |
| 50 | + |
| 51 | +/** Assume macro `f(x, y, ...)` expands to `__c11_f(x, y, ...)`. */ |
| 52 | +module C11FunctionWrapperMacro implements InferMacroExpansionArguments { |
| 53 | + bindingset[mi, argumentIdx] |
| 54 | + cpp::Expr inferArgument(cpp::MacroInvocation mi, int argumentIdx) { |
| 55 | + if mi.getExpr().(cpp::FunctionCall).getTarget().hasName("__c11_" + mi.getMacroName()) |
| 56 | + then result = mi.getExpr().(cpp::FunctionCall).getArgument(argumentIdx) |
| 57 | + else result = NoMacroExpansionInference::inferArgument(mi, argumentIdx) |
| 58 | + } |
| 59 | +} |
| 60 | + |
| 61 | +/** |
| 62 | + * A module to find calls to standard functions, or expansions of macros with the same name. |
| 63 | + * |
| 64 | + * To use this module, specify a name predicate and an inference strategy for correlating macro |
| 65 | + * expansions to macro arguments. |
| 66 | + * |
| 67 | + * For example: |
| 68 | + * |
| 69 | + * ```codeql |
| 70 | + * private string atomicInit() { result = "atomic_init" } |
| 71 | + * from StdFunctionOrMacro<C11FunctionWrapperMacro, atomicInit/0>::Call c |
| 72 | + * select c.getArgument(0) |
| 73 | + * ``` |
| 74 | + */ |
| 75 | +module StdFunctionOrMacro<InferMacroExpansionArguments InferExpansion, getName/0 getStdName> { |
| 76 | + final private class Expr = cpp::Expr; |
| 77 | + |
| 78 | + final private class FunctionCall = cpp::FunctionCall; |
| 79 | + |
| 80 | + final private class MacroInvocation = cpp::MacroInvocation; |
| 81 | + |
| 82 | + private newtype TStdCall = |
| 83 | + TStdFunctionCall(FunctionCall fc) { fc.getTarget().hasName(getStdName()) } or |
| 84 | + TStdMacroInvocation(MacroInvocation mi) { mi.getMacro().hasName(getStdName()) } |
| 85 | + |
| 86 | + /** |
| 87 | + * A call to a standard function or an expansion of a macro with the same name. |
| 88 | + */ |
| 89 | + class Call extends TStdCall { |
| 90 | + bindingset[this, argumentIdx] |
| 91 | + Expr getArgument(int argumentIdx) { |
| 92 | + exists(FunctionCall fc | |
| 93 | + this = TStdFunctionCall(fc) and |
| 94 | + result = fc.getArgument(argumentIdx) |
| 95 | + ) |
| 96 | + or |
| 97 | + exists(MacroInvocation mi | |
| 98 | + this = TStdMacroInvocation(mi) and |
| 99 | + result = InferExpansion::inferArgument(mi, argumentIdx) |
| 100 | + ) |
| 101 | + } |
| 102 | + |
| 103 | + string toString() { |
| 104 | + this = TStdFunctionCall(_) and |
| 105 | + result = "Standard function call" |
| 106 | + or |
| 107 | + this = TStdMacroInvocation(_) and |
| 108 | + result = "Invocation of a standard function implemented as a macro" |
| 109 | + } |
| 110 | + } |
| 111 | +} |
0 commit comments