/** * Provides classes for modeling accesses including variable accesses, enum * constant accesses and function accesses. */ import semmle.code.cpp.exprs.Expr import semmle.code.cpp.Variable import semmle.code.cpp.Enum private import semmle.code.cpp.dataflow.EscapesTree /** * A C/C++ access expression. This refers to a function (excluding function references in function call expressions), variable, or enum constant. */ class Access extends Expr, NameQualifiableElement, @access { // As `@access` is a union type containing `@routineexpr` (which describes function accesses // that are called), we need to exclude function calls. Access() { this instanceof @routineexpr implies not iscall(underlyingElement(this), _) } /** Gets the accessed function, variable, or enum constant. */ Declaration getTarget() { none() } // overridden in subclasses override predicate mayBeImpure() { none() } override predicate mayBeGloballyImpure() { none() } override string toString() { none() } } /** * A C/C++ `enum` constant access expression. For example the access to * `MYENUMCONST1` in `myFunction` in the following code: * ``` * enum MyEnum { * MYENUMCONST1, * MYENUMCONST2 * }; * * void myFunction() { * MyEnum v = MYENUMCONST1; * }; * ``` */ class EnumConstantAccess extends Access, @varaccess { override string getAPrimaryQlClass() { result = "EnumConstantAccess" } EnumConstantAccess() { exists(EnumConstant c | varbind(underlyingElement(this), unresolveElement(c))) } /** Gets the accessed `enum` constant. */ override EnumConstant getTarget() { varbind(underlyingElement(this), unresolveElement(result)) } /** Gets a textual representation of this `enum` constant access. */ override string toString() { result = this.getTarget().getName() } } /** * A C/C++ variable access expression. For example the accesses to * `x` and `y` in `myFunction` in the following code: * ``` * int x; * * void myFunction(int y) { * x = y; * }; * ``` */ class VariableAccess extends Access, @varaccess { override string getAPrimaryQlClass() { result = "VariableAccess" } VariableAccess() { not exists(EnumConstant c | varbind(underlyingElement(this), unresolveElement(c))) } /** Gets the accessed variable. */ override Variable getTarget() { varbind(underlyingElement(this), unresolveElement(result)) } /** * Holds if this variable access is providing an LValue in a meaningful way. * For example, this includes accesses on the left-hand side of an assignment. * It does not include accesses on the right-hand side of an assignment, even if they could appear on the left-hand side of some assignment. */ predicate isUsedAsLValue() { exists(Assignment a | a.getLValue() = this) or exists(CrementOperation c | c.getOperand() = this) or exists(AddressOfExpr addof | addof.getOperand() = this) or this.getConversion() instanceof ReferenceToExpr or this.getConversion() instanceof ArrayToPointerConversion } /** * Holds if this variable access is in a position where it is (directly) modified, * for instance by an assignment or increment/decrement operator. */ predicate isModified() { exists(Assignment a | a.getLValue() = this) or exists(CrementOperation c | c.getOperand() = this) or exists(FunctionCall c | c.getQualifier() = this and c.getTarget().hasName("operator=")) } /** Holds if this variable access is an rvalue. */ predicate isRValue() { not exists(AssignExpr ae | ae.getLValue() = this) and not exists(AddressOfExpr addof | addof.getOperand() = this) and not this.getConversion() instanceof ReferenceToExpr and not this.getConversion() instanceof ArrayToPointerConversion } /** * Gets the expression generating the variable being accessed. * * As a few examples: * For `ptr->x`, this gives `ptr`. * For `(*ptr).x`, this gives `(*ptr)`. * For `smart_ptr->x`, this gives the call to `operator->`. * * This applies mostly to FieldAccesses, but also to static member variables accessed * "through" a pointer. Note that it does NOT apply to static member variables accessed * through a type name, as in that case the type name is a qualifier on the variable * rather than a qualifier on the access. */ Expr getQualifier() { this.getChild(-1) = result } /** Gets a textual representation of this variable access. */ override string toString() { if exists(this.getTarget()) then result = this.getTarget().getName() else result = "variable access" } override predicate mayBeImpure() { this.getQualifier().mayBeImpure() or this.getTarget().getType().isVolatile() } override predicate mayBeGloballyImpure() { this.getQualifier().mayBeGloballyImpure() or this.getTarget().getType().isVolatile() } /** * Holds if this access is used to get the address of the underlying variable * in such a way that the address might escape. This can be either explicit, * for example `&x`, or implicit, for example `T& y = x`. */ predicate isAddressOfAccess() { variableAddressEscapesTree(this, _) } /** * Holds if this access is used to get the address of the underlying variable * in such a way that the address might escape as a pointer or reference to * non-const data. This can be either explicit, for example `&x`, or * implicit, for example `T& y = x`. */ predicate isAddressOfAccessNonConst() { variableAddressEscapesTreeNonConst(this, _) } } /** * A C/C++ field access expression. For example the accesses to * `x` and `y` in `myMethod` in the following code: * ``` * class MyClass { * public: * void myMethod(MyClass &other) { * x = other.y; * } * * int x, y; * }; * ``` */ class FieldAccess extends VariableAccess { override string getAPrimaryQlClass() { result = "FieldAccess" } FieldAccess() { exists(Field f | varbind(underlyingElement(this), unresolveElement(f))) } /** Gets the accessed field. */ override Field getTarget() { result = super.getTarget() } } /** * A field access whose qualifier is a pointer to a class, struct or union. * These typically take the form `obj->field`. Another case is a field access * with an implicit `this->` qualifier, which is often a `PointerFieldAccess` * (but see also `ImplicitThisFieldAccess`). * * For example the accesses to `x` and `y` in `myMethod` in the following code * are each a `PointerFieldAccess`: * ``` * class MyClass { * public: * void myMethod(MyClass *other) { * other->x = y; * } * * int x, y; * }; * ``` */ class PointerFieldAccess extends FieldAccess { override string getAPrimaryQlClass() { result = "PointerFieldAccess" } PointerFieldAccess() { exists(PointerType t | t = this.getQualifier().getFullyConverted().getUnspecifiedType() and t.getBaseType() instanceof Class ) } } /** * A field access of the form `obj.field`. The type of `obj` is either a * class/struct/union or a reference to one. `DotFieldAccess` has two * sub-classes, `ValueFieldAccess` and `ReferenceFieldAccess`, to * distinguish whether or not the type of `obj` is a reference type. */ class DotFieldAccess extends FieldAccess { override string getAPrimaryQlClass() { result = "DotFieldAccess" } DotFieldAccess() { this.getQualifier().getFullyConverted().getUnspecifiedType() instanceof Class } } /** * A field access of the form `obj.field`, where the type of `obj` is a * reference to a class/struct/union. For example the accesses to `y` in * `myMethod` in the following code: * ``` * class MyClass { * public: * void myMethod(MyClass a, MyClass &b) { * a.x = b.y; * } * * int x, y; * }; * ``` */ class ReferenceFieldAccess extends DotFieldAccess { override string getAPrimaryQlClass() { result = "ReferenceFieldAccess" } ReferenceFieldAccess() { exprHasReferenceConversion(this.getQualifier()) } } /** * A field access of the form `obj.field`, where the type of `obj` is a * class/struct/union (and not a reference). For example the accesses to `x` * in `myMethod` in the following code: * ``` * class MyClass { * public: * void myMethod(MyClass a, MyClass &b) { * a.x = b.y; * } * * int x, y; * }; * ``` */ class ValueFieldAccess extends DotFieldAccess { override string getAPrimaryQlClass() { result = "ValueFieldAccess" } ValueFieldAccess() { not exprHasReferenceConversion(this.getQualifier()) } } /** * Holds if `c` is a conversion from type `T&` to `T` (or from `T&&` to * `T`). */ private predicate referenceConversion(Conversion c) { c.getType() = c.getExpr().getType().(ReferenceType).getBaseType() } /** * Holds if `e` is a reference expression (that is, it has a type of the * form `T&`), which is converted to a value. For example: * ``` * int myfcn(MyStruct &x) { * return x.field; * } * ``` * In this example, the type of `x` is `MyStruct&`, but it gets implicitly * converted to `MyStruct` in the expression `x.field`. */ private predicate exprHasReferenceConversion(Expr e) { referenceConversion(e.getConversion+()) } /** * A field access of a field of `this` which has no qualifier because * the use of `this` is implicit. For example, in the following code the * implicit call to the destructor of `A` has no qualifier because the * use of `this` is implicit: * ``` * class A { * public: * ~A() { * // ... * } * }; * * class B { * public: * A a; * * ~B() { * // Implicit call to the destructor of `A`. * } * }; * ``` */ class ImplicitThisFieldAccess extends FieldAccess { override string getAPrimaryQlClass() { result = "ImplicitThisFieldAccess" } ImplicitThisFieldAccess() { this.getQualifier().(ThisExpr).isCompilerGenerated() or not exists(this.getQualifier()) } } /** * A C++ _pointer to non-static data member_ literal. For example, `&C::x` is * an expression that refers to field `x` of class `C`. If the type of that * field is `int`, then `&C::x` ought to have type `int C::*`. It is currently * modeled in QL as having type `int`. * * See [dcl.mptr] in the C++17 standard or see * https://en.cppreference.com/w/cpp/language/pointer#Pointers_to_data_members. */ class PointerToFieldLiteral extends ImplicitThisFieldAccess { PointerToFieldLiteral() { // The extractor currently emits a pointer-to-field literal as a field // access without a qualifier. The only other unqualified field accesses it // emits are for compiler-generated constructors and destructors. When we // filter those out, there are only pointer-to-field literals left. not this.isCompilerGenerated() and not exists(this.getQualifier()) } override predicate isConstant() { any() } override string getAPrimaryQlClass() { result = "PointerToFieldLiteral" } } /** * A C/C++ function access expression. For example the access to * `myFunctionTarget` in `myFunction` in the following code: * ``` * int myFunctionTarget(int); * * void myFunction() { * int (*myFunctionPointer)(int) = &myFunctionTarget; * } * ``` * This excludes function accesses in function call expressions. * For example the access `myFunctionTarget` in `myFunction` in the following code: * ``` * int myFunctionTarget(int); * * void myFunction() { * myFunctionTarget(1); * } * ``` */ class FunctionAccess extends Access, @routineexpr { FunctionAccess() { not iscall(underlyingElement(this), _) } override string getAPrimaryQlClass() { result = "FunctionAccess" } /** Gets the accessed function. */ override Function getTarget() { funbind(underlyingElement(this), unresolveElement(result)) } /** * Gets the expression generating the function being accessed. */ Expr getQualifier() { this.getChild(-1) = result } /** Gets a textual representation of this function access. */ override string toString() { if exists(this.getTarget()) then result = this.getTarget().getName() else result = "function access" } } /** * An access to a parameter of a function signature for the purposes of a `decltype`. * * For example, given the following code: * ``` * template * auto add(L lhs, R rhs) -> decltype(lhs + rhs) { * return lhs + rhs; * } * ``` * The return type of the function is a decltype, the expression of which contains * an add expression, which in turn has two `ParamAccessForType` children. */ class ParamAccessForType extends Expr, @param_ref { override string toString() { result = "param access" } /** * Holds if the accessed parameter is implicit object parameter of the function. */ predicate isThisAccess() { param_ref_to_this(underlyingElement(this)) } } /** * An access to a type. This occurs in certain contexts where a built-in * works on types directly rather than variables, expressions etc. For * example the reference to `MyClass` in `__is_pod` in the following code: * ``` * class MyClass { * ... * }; * * void myFunction() { * if (__is_pod(MyClass)) * { * ... * } else { * ... * } * } * ``` */ class TypeName extends Expr, @type_operand { override string getAPrimaryQlClass() { result = "TypeName" } override string toString() { result = this.getType().getName() } } /** * A C/C++ array access expression. For example, the access to `as` in * `myFunction` in the following code: * ``` * int as[10]; * * void myFunction() { * as[0]++; * } * ``` * For calls to `operator[]`, which look syntactically identical, see * `OverloadedArrayExpr`. */ class ArrayExpr extends Expr, @subscriptexpr { override string getAPrimaryQlClass() { result = "ArrayExpr" } /** * Gets the array or pointer expression being subscripted. * * This is `arr` in both `arr[0]` and `0[arr]`. */ Expr getArrayBase() { result = this.getChild(0) } /** * Gets the expression giving the index into the array. * * This is `0` in both `arr[0]` and `0[arr]`. */ Expr getArrayOffset() { result = this.getChild(1) } /** * Holds if this array access is in a position where it is (directly) modified, * for instance by an assignment or an increment/decrement operation. */ predicate isModified() { exists(Assignment a | a.getLValue() = this) or exists(CrementOperation c | c.getOperand() = this) or exists(FunctionCall c | c.getQualifier() = this and c.getTarget().hasName("operator=")) } override string toString() { result = "access to array" } override predicate mayBeImpure() { this.getArrayBase().mayBeImpure() or this.getArrayOffset().mayBeImpure() or this.getArrayBase().getFullyConverted().getType().(DerivedType).getBaseType().isVolatile() or this.getArrayOffset().getFullyConverted().getType().(DerivedType).getBaseType().isVolatile() } override predicate mayBeGloballyImpure() { this.getArrayBase().mayBeGloballyImpure() or this.getArrayOffset().mayBeGloballyImpure() or this.getArrayBase().getFullyConverted().getType().(DerivedType).getBaseType().isVolatile() or this.getArrayOffset().getFullyConverted().getType().(DerivedType).getBaseType().isVolatile() } }