Files
codeql/cpp/ql/lib/semmle/code/cpp/exprs/Call.qll
2022-01-20 10:41:35 +01:00

666 lines
22 KiB
Plaintext

/**
* Provides classes for modeling call expressions including direct calls to
* functions, constructor and destructor calls, and calls made through function
* pointers.
*/
import semmle.code.cpp.exprs.Expr
import semmle.code.cpp.Function
private import semmle.code.cpp.dataflow.EscapesTree
private class TCall = @funbindexpr or @callexpr;
/**
* A C/C++ call.
*/
class Call extends Expr, NameQualifiableElement, TCall {
// `@funbindexpr` (which is the dbscheme type for FunctionCall) is a union type that includes
// `@routineexpr. This dbscheme type includes accesses to functions that are not necessarily calls to
// that function. That's why the charpred for `FunctionCall` requires:
// ```
// iscall(underlyingElement(this), _)
// ```
// So for the charpred for `Call` we include the requirement that if this is an instance of
// `@funbindexpr` it must be a _call_ to the function.
Call() { this instanceof @callexpr or iscall(underlyingElement(this), _) }
/**
* Gets the number of arguments (actual parameters) of this call. The count
* does _not_ include the qualifier of the call, if any.
*/
int getNumberOfArguments() { result = count(this.getAnArgument()) }
/**
* Holds if this call has a qualifier.
*
* For example, `ptr->f()` has a qualifier, whereas plain `f()` does not.
*/
predicate hasQualifier() { exists(this.getChild(-1)) }
/**
* Gets the expression to the left of the function name or function pointer variable name.
*
* As a few examples:
* For the call to `f` in `ptr->f()`, this gives `ptr`.
* For the call to `f` in `(*ptr).f()`, this gives `(*ptr)`.
*/
Expr getQualifier() { result = this.getChild(-1) }
/**
* Gets an argument for this call. To get the qualifier of this call, if
* any, use `getQualifier()`.
*/
Expr getAnArgument() { exists(int i | result = this.getChild(i) and i >= 0) }
/**
* Gets the nth argument for this call.
*
* The range of `n` is from `0` to `getNumberOfArguments() - 1`. To get the
* qualifier of this call, if any, use `getQualifier()`.
*/
Expr getArgument(int n) { result = this.getChild(n) and n >= 0 }
/**
* Gets a subexpression of the argument at position `index`. If the
* argument itself contains calls, such calls will be considered
* leaves in the expression tree. The qualifier of the call, if any, is not
* considered to be an argument.
*
* Example: the call `f(2, 3 + 4, g(4 + 5))` has sub expression(s)
* `2` at index 0; `3`, `4`, and `3 + 4` at index 1; and `g(4 + 5)`
* at index 2, respectively.
*/
Expr getAnArgumentSubExpr(int index) {
result = this.getArgument(index)
or
exists(Expr mid |
mid = this.getAnArgumentSubExpr(index) and
not mid instanceof Call and
not mid instanceof SizeofOperator and
result = mid.getAChild()
)
}
/**
* Gets the target of the call, as best as makes sense for this kind of call.
* The precise meaning depends on the kind of call it is:
* - For a call to a function, it's the function being called.
* - For a C++ method call, it's the statically resolved method.
* - For an Objective C message expression, it's the statically resolved
* method, and it might not exist.
* - For a variable call, it never exists.
*/
Function getTarget() { none() } // overridden in subclasses
override int getPrecedence() { result = 17 }
override string toString() { none() }
/**
* Holds if this call passes the variable accessed by `va` by
* reference as the `i`th argument. The qualifier of a call to a member
* function is `i = -1`.
*
* A variable is passed by reference if the `i`th parameter of the function
* receives an address that points within the object denoted by `va`. For a
* variable named `x`, passing by reference includes both explicit pointers
* (`&x`) and implicit conversion to a C++ reference (`x`), but it also
* includes deeper expressions such as `&x[0] + length` or `&*&*&x`.
*
* When `Field`s are involved, an argument `i` may pass more than one
* variable by reference simultaneously. For example, the call `f(&x.m1.m2)`
* counts as passing both `x`, `m1` and `m2` to argument 0 of `f`.
*
* This predicate holds for variables passed by reference even if they are
* passed as references to `const` and thus cannot be changed through that
* reference. See `passesByNonConstReference` for a predicate that only holds
* for variables passed by reference to non-const.
*/
predicate passesByReference(int i, VariableAccess va) {
variableAddressEscapesTree(va, this.getArgument(i).getFullyConverted())
or
variableAddressEscapesTree(va, this.getQualifier().getFullyConverted()) and
i = -1
}
/**
* Holds if this call passes the variable accessed by `va` by
* reference to non-const data as the `i`th argument. The qualifier of a
* call to a member function is `i = -1`.
*
* A variable is passed by reference if the `i`th parameter of the function
* receives an address that points within the object denoted by `va`. For a
* variable named `x`, passing by reference includes both explicit pointers
* (`&x`) and implicit conversion to a C++ reference (`x`), but it also
* includes deeper expressions such as `&x[0] + length` or `&*&*&x`.
*
* When `Field`s are involved, an argument `i` may pass more than one
* variable by reference simultaneously. For example, the call `f(&x.m1.m2)`
* counts as passing both `x`, `m1` and `m2` to argument 0 of `f`.
*
* This predicate only holds for variables passed by reference to non-const
* data and thus can be changed through that reference. See
* `passesByReference` for a predicate that also holds for variables passed
* by reference to const.
*/
predicate passesByReferenceNonConst(int i, VariableAccess va) {
variableAddressEscapesTreeNonConst(va, this.getArgument(i).getFullyConverted())
or
variableAddressEscapesTreeNonConst(va, this.getQualifier().getFullyConverted()) and
i = -1
}
}
/**
* A C/C++ function call where the name of the target function is known at compile-time.
*
* This includes various kinds of call:
* 1. Calls such as `f(x)` where `f` is the name of a function.
* 2. Calls such as `ptr->f()` where `f` is the name of a (possibly virtual) member function.
* 3. Constructor calls for stack-allocated objects.
* 4. Implicit and explicit calls to user-defined operators.
* 5. Base class initializers in constructors.
*/
class FunctionCall extends Call, @funbindexpr {
FunctionCall() { iscall(underlyingElement(this), _) }
override string getAPrimaryQlClass() { result = "FunctionCall" }
/** Gets an explicit template argument for this call. */
Locatable getAnExplicitTemplateArgument() { result = this.getExplicitTemplateArgument(_) }
/** Gets an explicit template argument value for this call. */
Locatable getAnExplicitTemplateArgumentKind() { result = this.getExplicitTemplateArgumentKind(_) }
/** Gets a template argument for this call. */
Locatable getATemplateArgument() { result = this.getTarget().getATemplateArgument() }
/** Gets a template argument value for this call. */
Locatable getATemplateArgumentKind() { result = this.getTarget().getATemplateArgumentKind() }
/** Gets the nth explicit template argument for this call. */
Locatable getExplicitTemplateArgument(int n) {
n < this.getNumberOfExplicitTemplateArguments() and
result = this.getTemplateArgument(n)
}
/** Gets the nth explicit template argument value for this call. */
Locatable getExplicitTemplateArgumentKind(int n) {
n < this.getNumberOfExplicitTemplateArguments() and
result = this.getTemplateArgumentKind(n)
}
/** Gets the number of explicit template arguments for this call. */
int getNumberOfExplicitTemplateArguments() {
if numtemplatearguments(underlyingElement(this), _)
then numtemplatearguments(underlyingElement(this), result)
else result = 0
}
/** Gets the number of template arguments for this call. */
int getNumberOfTemplateArguments() { result = count(int i | exists(this.getTemplateArgument(i))) }
/** Gets the nth template argument for this call (indexed from 0). */
Locatable getTemplateArgument(int n) { result = this.getTarget().getTemplateArgument(n) }
/** Gets the nth template argument value for this call (indexed from 0). */
Locatable getTemplateArgumentKind(int n) { result = this.getTarget().getTemplateArgumentKind(n) }
/** Holds if any template arguments for this call are implicit / deduced. */
predicate hasImplicitTemplateArguments() {
exists(int i |
exists(this.getTemplateArgument(i)) and
not exists(this.getExplicitTemplateArgument(i))
)
}
/** Holds if a template argument list was provided for this call. */
predicate hasTemplateArgumentList() { numtemplatearguments(underlyingElement(this), _) }
/**
* Gets the `RoutineType` of the call target as visible at the call site. For
* constructor calls, this predicate instead gets the `Class` of the constructor
* being called.
*/
Type getTargetType() { result = Call.super.getType().stripType() }
/**
* Gets the expected return type of the function called by this call.
*
* In most cases, the expected return type will be the return type of the function being called.
* It is only different when the function being called is ambiguously declared, at which point
* the expected return type is the return type of the (unambiguous) function declaration that was
* visible at the call site.
*/
Type getExpectedReturnType() {
if this.getTargetType() instanceof RoutineType
then result = this.getTargetType().(RoutineType).getReturnType()
else result = this.getTarget().getType()
}
/**
* Gets the expected type of the nth parameter of the function called by this call.
*
* In most cases, the expected parameter types match the parameter types of the function being called.
* They are only different when the function being called is ambiguously declared, at which point
* the expected parameter types are the parameter types of the (unambiguous) function declaration that
* was visible at the call site.
*/
Type getExpectedParameterType(int n) {
if this.getTargetType() instanceof RoutineType
then result = this.getTargetType().(RoutineType).getParameterType(n)
else result = this.getTarget().getParameter(n).getType()
}
/**
* Gets the function called by this call.
*
* In the case of virtual function calls, the result is the most-specific function in the override tree (as
* determined by the compiler) such that the target at runtime will be one of `result.getAnOverridingFunction*()`.
*/
override Function getTarget() { funbind(underlyingElement(this), unresolveElement(result)) }
/**
* Gets the type of this expression, that is, the return type of the function being called.
*/
override Type getType() { result = this.getExpectedReturnType() }
/**
* Holds if this is a call to a virtual function.
*
* Note that this holds even in cases where a sufficiently clever compiler could perform static dispatch.
*/
predicate isVirtual() { iscall(underlyingElement(this), 1) }
/**
* Holds if the target of this function call was found by argument-dependent lookup and wouldn't have been
* found by any other means.
*/
predicate isOnlyFoundByADL() { iscall(underlyingElement(this), 2) }
/** Gets a textual representation of this function call. */
override string toString() {
if exists(this.getTarget())
then result = "call to " + this.getTarget().getName()
else result = "call to unknown function"
}
override predicate mayBeImpure() {
this.getChild(_).mayBeImpure() or
this.getTarget().mayHaveSideEffects() or
this.isVirtual() or
this.getTarget().getAnAttribute().getName() = "weak"
}
override predicate mayBeGloballyImpure() {
this.getChild(_).mayBeGloballyImpure() or
this.getTarget().mayHaveSideEffects() or
this.isVirtual() or
this.getTarget().getAnAttribute().getName() = "weak"
}
}
/** A _user-defined_ unary `operator*` function. */
class OverloadedPointerDereferenceFunction extends Function {
OverloadedPointerDereferenceFunction() {
this.hasName("operator*") and
this.getEffectiveNumberOfParameters() = 1
}
}
/**
* An instance of a _user-defined_ unary `operator*` applied to its argument.
* ```
* T1 operator*(const T2 &);
* T1 a; T2 b;
* a = *b;
* ```
*/
class OverloadedPointerDereferenceExpr extends FunctionCall {
OverloadedPointerDereferenceExpr() {
this.getTarget() instanceof OverloadedPointerDereferenceFunction
}
override string getAPrimaryQlClass() { result = "OverloadedPointerDereferenceExpr" }
/**
* Gets the expression this operator * applies to.
*/
Expr getExpr() {
result = this.getChild(0) or
result = this.getQualifier()
}
override predicate mayBeImpure() {
FunctionCall.super.mayBeImpure() and
(
this.getExpr().mayBeImpure()
or
not exists(Class declaring |
this.getTarget().getDeclaringType().isConstructedFrom*(declaring)
|
declaring.getNamespace() instanceof StdNamespace
)
)
}
override predicate mayBeGloballyImpure() {
FunctionCall.super.mayBeGloballyImpure() and
(
this.getExpr().mayBeGloballyImpure()
or
not exists(Class declaring |
this.getTarget().getDeclaringType().isConstructedFrom*(declaring)
|
declaring.getNamespace() instanceof StdNamespace
)
)
}
}
/**
* An instance of a _user-defined_ binary `operator[]` applied to its arguments.
* ```
* struct T2 { T1 operator[](const T3 &); };
* T1 a; T2 b; T3 c;
* a = b[c];
* ```
*/
class OverloadedArrayExpr extends FunctionCall {
OverloadedArrayExpr() { this.getTarget().hasName("operator[]") }
override string getAPrimaryQlClass() { result = "OverloadedArrayExpr" }
/**
* Gets the expression being subscripted.
*/
Expr getArrayBase() {
if exists(this.getQualifier()) then result = this.getQualifier() else result = this.getChild(0)
}
/**
* Gets the expression giving the index.
*/
Expr getArrayOffset() {
if exists(this.getQualifier()) then result = this.getChild(0) else result = this.getChild(1)
}
}
/**
* A C/C++ call which is performed through a function pointer.
*
* In the call below, `(*funcptr)` may be simplified to just `funcptr`.
* ```
* extern int (*funcptr)(int a, int b);
* int c = (*funcptr)(1, 2);
* ```
*/
class ExprCall extends Call, @callexpr {
/**
* Gets the expression which yields the function pointer to call.
*/
Expr getExpr() { result = this.getChild(0) }
override string getAPrimaryQlClass() { result = "ExprCall" }
override Expr getAnArgument() { exists(int i | result = this.getChild(i) and i >= 1) }
override Expr getArgument(int index) {
result = this.getChild(index + 1) and index in [0 .. this.getNumChild() - 2]
}
override string toString() { result = "call to expression" }
override Function getTarget() { none() }
}
/**
* A C/C++ call which is performed through a variable of function pointer type.
* ```
* int call_via_ptr(int (*pfn)(int)) {
* return pfn(5);
* }
* ```
*/
class VariableCall extends ExprCall {
VariableCall() { this.getExpr() instanceof VariableAccess }
override string getAPrimaryQlClass() { result = "VariableCall" }
/**
* Gets the variable which yields the function pointer to call.
*/
Variable getVariable() { this.getExpr().(VariableAccess).getTarget() = result }
}
/**
* A call to a constructor.
* ```
* struct S { S(void) {} };
* S s;
* ```
*/
class ConstructorCall extends FunctionCall {
ConstructorCall() { super.getTarget() instanceof Constructor }
override string getAPrimaryQlClass() { result = "ConstructorCall" }
/** Gets the constructor being called. */
override Constructor getTarget() { result = super.getTarget() }
}
/**
* A call to a destructor.
* ```
* struct S { ~S(void) {} } *s;
* s->~S();
* ```
*/
class DestructorCall extends FunctionCall {
DestructorCall() { super.getTarget() instanceof Destructor }
override string getAPrimaryQlClass() { result = "DestructorCall" }
/** Gets the destructor being called. */
override Destructor getTarget() { result = super.getTarget() }
}
/**
* An expression that looks like a destructor call, but has no effect.
*
* For example, given a plain old data type `pod_t`, the syntax `ptr->~pod_t()` is
* a vacuous destructor call, as `~pod_t` isn't actually a function. This can also
* occur in instantiated templates, as `ptr->~T()` becomes vacuous when `T` is `int`.
* ```
* typedef int pod_t;
* pod_t *s;
* s->~pod_t();
* ```
*/
class VacuousDestructorCall extends Expr, @vacuous_destructor_call {
/**
* Gets the expression for the object whose destructor would be called.
*/
Expr getQualifier() { result = this.getChild(0) }
override string getAPrimaryQlClass() { result = "VacuousDestructorCall" }
override string toString() { result = "(vacuous destructor call)" }
}
/**
* An initialization of a base class or member variable performed as part
* of a constructor's explicit initializer list or implicit actions.
*
* This is a QL root class for reprenting various types of constructor
* initializations.
*/
class ConstructorInit extends Expr, @ctorinit {
override string getAPrimaryQlClass() { result = "ConstructorInit" }
}
/**
* A call to a constructor of a base class as part of a constructor's
* initializer list or compiler-generated actions.
*/
class ConstructorBaseInit extends ConstructorInit, ConstructorCall {
override string getAPrimaryQlClass() { result = "ConstructorBaseInit" }
}
/**
* A call to a constructor of a direct non-virtual base class as part of a
* constructor's initializer list or compiler-generated actions.
* ```
* struct S {
* int a;
* S(int b): a(b) {}
* };
* struct T: S {
* T(): S(33) {} // S(33) is a constructor call
* };
* ```
*/
class ConstructorDirectInit extends ConstructorBaseInit, @ctordirectinit {
override string getAPrimaryQlClass() { result = "ConstructorDirectInit" }
}
/**
* A call to a constructor of a virtual base class as part of a
* constructor's initializer list or compiler-generated actions.
*
* If the virtual base class has already been initialized, then this
* call won't be performed.
* ```
* struct S {
* int a;
* S(int b): a(b) {}
* };
* struct T: virtual S {
* T(): S(33) {} // S(33) is a call to a virtual base constructor
* };
* ```
*/
class ConstructorVirtualInit extends ConstructorBaseInit, @ctorvirtualinit {
override string getAPrimaryQlClass() { result = "ConstructorVirtualInit" }
}
/**
* A call to a constructor of the same class as part of a constructor's
* initializer list, which delegates object construction (C++11 only).
* ```
* struct S {
* int a;
* S(int b): a(b) { }
* S(): S(0) { } // delegation to another constructor
* };
* ```
*/
class ConstructorDelegationInit extends ConstructorBaseInit, @ctordelegatinginit {
override string getAPrimaryQlClass() { result = "ConstructorDelegationInit" }
}
/**
* An initialization of a member variable performed as part of a
* constructor's explicit initializer list or implicit actions.
* In the example below, member variable `b` is being initialized by
* constructor parameter `a`:
* ```
* struct S {
* int b;
* S(int a): b(a) {}
* } s(2);
* ```
*/
class ConstructorFieldInit extends ConstructorInit, @ctorfieldinit {
/** Gets the field being initialized. */
Field getTarget() { varbind(underlyingElement(this), unresolveElement(result)) }
override string getAPrimaryQlClass() { result = "ConstructorFieldInit" }
/**
* Gets the expression to which the field is initialized.
*
* This is typically either a Literal or a FunctionCall to a
* constructor, but more complex expressions can also occur.
*/
Expr getExpr() { result = this.getChild(0) }
override string toString() { result = "constructor init of field " + this.getTarget().getName() }
override predicate mayBeImpure() { this.getExpr().mayBeImpure() }
override predicate mayBeGloballyImpure() { this.getExpr().mayBeGloballyImpure() }
}
/**
* A call to a destructor of a base class or field as part of a destructor's
* compiler-generated actions.
*/
class DestructorDestruction extends Expr, @dtordestruct {
override string getAPrimaryQlClass() { result = "DestructorDestruction" }
}
/**
* A call to a destructor of a base class as part of a destructor's
* compiler-generated actions.
*/
class DestructorBaseDestruction extends DestructorCall, DestructorDestruction {
override string getAPrimaryQlClass() { result = "DestructorBaseDestruction" }
}
/**
* A call to a destructor of a direct non-virtual base class as part of a
* destructor's compiler-generated actions.
* ```
* struct S { ~S(void) {} };
* struct T: S {
* ~T(void) {} // will call ~S()
* };
* ```
*/
class DestructorDirectDestruction extends DestructorBaseDestruction, @dtordirectdestruct {
override string getAPrimaryQlClass() { result = "DestructorDirectDestruction" }
}
/**
* A call to a destructor of a direct virtual base class as part of a
* destructor's compiler-generated actions.
*
* If the virtual base class wasn't initialized by the ConstructorVirtualInit
* in the corresponding constructor, then this call won't be performed.
* ```
* struct S { ~S(void) {} };
* struct T: virtual S {
* ~T(void) {} // will call ~S()
* };
* ```
*/
class DestructorVirtualDestruction extends DestructorBaseDestruction, @dtorvirtualdestruct {
override string getAPrimaryQlClass() { result = "DestructorVirtualDestruction" }
}
/**
* A destruction of a member variable performed as part of a
* destructor's compiler-generated actions.
* ```
* struct S { ~S(void) {} };
* struct T {
* S s;
* ~T(void) {} // will call s.~S()
* };
* ```
*/
class DestructorFieldDestruction extends DestructorDestruction, @dtorfielddestruct {
/** Gets the field being destructed. */
Field getTarget() { varbind(underlyingElement(this), unresolveElement(result)) }
override string getAPrimaryQlClass() { result = "DestructorFieldDestruction" }
/** Gets the compiler-generated call to the variable's destructor. */
DestructorCall getExpr() { result = this.getChild(0) }
override string toString() {
result = "destructor field destruction of " + this.getTarget().getName()
}
}