Files
codeql/cpp/ql/lib/semmle/code/cpp/security/FunctionWithWrappers.qll
Erik Krogh Kristensen fe891746bf C++: fix implicit this
2021-10-15 14:59:48 +01:00

180 lines
6.5 KiB
Plaintext

/**
* Provides predicates for identifying functions that wrap other functions,
* passing the same arguments from the outer call into the inner call. In the
* following example `MyMalloc` wraps a call to `malloc`, passing in the `size`
* parameter:
* ```
* void *MyMalloc(size_t size)
* {
* void *ptr = malloc(size);
*
* // ... additional logic?
*
* return ptr;
* }
* ```
*/
import cpp
import PrintfLike
private import semmle.code.cpp.ir.dataflow.ResolveCall
bindingset[index]
private string toCause(Function func, int index) {
result = func.getQualifiedName() + "(" + func.getParameter(index).getName() + ")"
or
not exists(func.getParameter(index).getName()) and
result = func.getQualifiedName() + "(arg " + index + ")"
}
/**
* Whether the parameter at index 'sourceParamIndex' of function 'source' is passed
* (without any evident changes) to the parameter at index 'targetParamIndex' of function 'target'.
*/
private predicate wrapperFunctionStep(
Function source, int sourceParamIndex, Function target, int targetParamIndex
) {
not target.isVirtual() and
not source.isVirtual() and
source.hasDefinition() and
exists(Call call, Expr arg, Parameter sourceParam |
// there is a 'call' to 'target' with argument 'arg' at index 'targetParamIndex'
target = resolveCall(call) and
arg = call.getArgument(targetParamIndex) and
// 'call' is enclosed in 'source'
source = call.getEnclosingFunction() and
// 'arg' is an access to the parameter at index 'sourceParamIndex' of function 'source'
sourceParam = source.getParameter(sourceParamIndex) and
not exists(sourceParam.getAnAssignedValue()) and
arg = sourceParam.getAnAccess()
)
}
/**
* An abstract class for representing functions that may have wrapper functions.
* Wrapper functions propagate an argument (without any evident changes) to this function
* through one or more steps in a call chain.
*
* The design motivation is to report a violation at the location of the argument
* in a call to the wrapper function rather than the function being wrapped, since
* that is usually the more appropriate place to fix the violation.
*
* Subclasses should override the characteristic predicate and 'interestingArg'.
*/
abstract class FunctionWithWrappers extends Function {
/**
* Which argument indices are relevant for wrapper function detection.
*/
predicate interestingArg(int arg) { none() }
/**
* Whether 'func' is a (possibly nested) wrapper function that feeds a parameter at the given index
* through to an interesting parameter of 'this' function at the given call chain 'depth'.
* The call chain depth is limited to 4.
*/
private predicate wrapperFunctionLimitedDepth(
Function func, int paramIndex, string callChain, int depth
) {
// base case
func = this and
this.interestingArg(paramIndex) and
callChain = toCause(func, paramIndex) and
depth = 0
or
// recursive step
exists(Function target, int targetParamIndex, string targetCause, int targetDepth |
this.wrapperFunctionLimitedDepth(target, targetParamIndex, targetCause, targetDepth) and
targetDepth < 4 and
wrapperFunctionStep(func, paramIndex, target, targetParamIndex) and
callChain = toCause(func, paramIndex) + ", which calls " + targetCause and
depth = targetDepth + 1
)
}
/**
* Whether 'func' is a (possibly nested) wrapper function that feeds a parameter at the given index
* through to an interesting parameter of 'this' function.
*
* The 'cause' gives the name of 'this' interesting function and its relevant parameter
* at the end of the call chain.
*/
private predicate wrapperFunctionAnyDepth(Function func, int paramIndex, string cause) {
// base case
func = this and
this.interestingArg(paramIndex) and
cause = toCause(func, paramIndex)
or
// recursive step
exists(Function target, int targetParamIndex |
this.wrapperFunctionAnyDepth(target, targetParamIndex, cause) and
wrapperFunctionStep(func, paramIndex, target, targetParamIndex)
)
}
/**
* Whether 'func' is a (possibly nested) wrapper function that feeds a parameter at the given index
* through to an interesting parameter of 'this' function.
*
* The 'cause' gives the name of 'this' interesting function and its relevant parameter
* at the end of the call chain.
*
* If there is more than one possible 'cause', a unique one is picked (by lexicographic order).
*/
pragma[nomagic]
private string wrapperFunctionAnyDepthUnique(Function func, int paramIndex) {
result =
toCause(func, paramIndex) + ", which ends up calling " +
min(string targetCause | this.wrapperFunctionAnyDepth(func, paramIndex, targetCause))
}
/**
* Whether 'func' is a (possibly nested) wrapper function that feeds a parameter at the given index
* through to an interesting parameter of 'this' function.
*
* If there exists a call chain with depth at most 4, the 'cause' reports the smallest call chain.
* Otherwise, the 'cause' merely reports the name of 'this' interesting function and its relevant
* parameter at the end of the call chain.
*
* If there is more than one possible 'cause', a unique one is picked (by lexicographic order).
*/
predicate wrapperFunction(Function func, int paramIndex, string cause) {
cause =
min(string callChain, int depth |
this.wrapperFunctionLimitedDepth(func, paramIndex, callChain, depth) and
depth = min(int d | this.wrapperFunctionLimitedDepth(func, paramIndex, _, d) | d)
|
callChain
)
or
not this.wrapperFunctionLimitedDepth(func, paramIndex, _, _) and
cause = this.wrapperFunctionAnyDepthUnique(func, paramIndex)
}
/**
* Whether 'arg' is an argument in a call to an outermost wrapper function of 'this' function.
*/
predicate outermostWrapperFunctionCall(Expr arg, string callChain) {
exists(Function targetFunc, Call call, int argIndex |
targetFunc = resolveCall(call) and
this.wrapperFunction(targetFunc, argIndex, callChain) and
(
exists(Function sourceFunc | sourceFunc = call.getEnclosingFunction() |
not wrapperFunctionStep(sourceFunc, _, targetFunc, argIndex)
)
or
not exists(call.getEnclosingFunction())
) and
arg = call.getArgument(argIndex)
)
}
}
/**
* A `printf`-like formatting function.
*/
class PrintfLikeFunction extends FunctionWithWrappers {
PrintfLikeFunction() { printfLikeFunction(this, _) }
override predicate interestingArg(int arg) { printfLikeFunction(this, arg) }
}