|
| 1 | +private import cpp |
| 2 | +private import semmle.code.cpp.dataflow.EscapesTree |
| 3 | + |
| 4 | +predicate addressConstantExpression(Expr e) { |
| 5 | + constantAddressPointer(e) |
| 6 | + or |
| 7 | + constantAddressReference(e) |
| 8 | + or |
| 9 | + // Special case for function pointers, where `fp == *fp`. |
| 10 | + constantAddressLValue(e) and |
| 11 | + e.getType() instanceof FunctionPointerType |
| 12 | +} |
| 13 | + |
| 14 | +/** Holds if `v` is a constexpr variable initialized to a constant address. */ |
| 15 | +private predicate addressConstantVariable(Variable v) { |
| 16 | + addressConstantExpression(v.getInitializer().getExpr().getFullyConverted()) and |
| 17 | + // Here we should also require that `v` is constexpr, but we don't have that |
| 18 | + // information in the db. See CPP-314. Instead, we require that the variable |
| 19 | + // is never defined except in its initializer. |
| 20 | + forall(Expr def | definition(v, def) | def = any(Initializer init).getExpr()) |
| 21 | +} |
| 22 | + |
| 23 | +/** |
| 24 | + * Holds if `lvalue` is an lvalue whose address is an _address constant |
| 25 | + * expression_. |
| 26 | + */ |
| 27 | +private predicate constantAddressLValue(Expr lvalue) { |
| 28 | + lvalue.(VariableAccess).getTarget() = any(Variable v | |
| 29 | + v.(Variable).isStatic() |
| 30 | + or |
| 31 | + v instanceof GlobalOrNamespaceVariable |
| 32 | + ) |
| 33 | + or |
| 34 | + // There is no `Conversion` for the implicit conversion from a function type |
| 35 | + // to a function _pointer_ type. Instead, the type of a `FunctionAccess` |
| 36 | + // tells us how it's going to be used. |
| 37 | + lvalue.(FunctionAccess).getType() instanceof RoutineType |
| 38 | + or |
| 39 | + // String literals have array types and undergo array-to-pointer conversion. |
| 40 | + lvalue instanceof StringLiteral |
| 41 | + or |
| 42 | + // lvalue -> lvalue |
| 43 | + exists(Expr prev | |
| 44 | + constantAddressLValue(prev) and |
| 45 | + lvalueToLvalueStep(prev, lvalue) |
| 46 | + ) |
| 47 | + or |
| 48 | + // pointer -> lvalue |
| 49 | + exists(Expr prev | |
| 50 | + constantAddressPointer(prev) and |
| 51 | + pointerToLvalueStep(prev, lvalue) |
| 52 | + ) |
| 53 | + or |
| 54 | + // reference -> lvalue |
| 55 | + exists(Expr prev | |
| 56 | + constantAddressReference(prev) and |
| 57 | + referenceToLvalueStep(prev, lvalue) |
| 58 | + ) |
| 59 | +} |
| 60 | + |
| 61 | +/** Holds if `pointer` is an _address constant expression_ of pointer type. */ |
| 62 | +private predicate constantAddressPointer(Expr pointer) { |
| 63 | + // There is no `Conversion` for the implicit conversion from a function type |
| 64 | + // to a function _pointer_ type. Instead, the type of a `FunctionAccess` |
| 65 | + // tells us how it's going to be used. |
| 66 | + pointer.(FunctionAccess).getType() instanceof FunctionPointerType |
| 67 | + or |
| 68 | + addressConstantVariable(pointer.(VariableAccess).getTarget()) and |
| 69 | + pointer.getType().getUnderlyingType() instanceof PointerType |
| 70 | + or |
| 71 | + // pointer -> pointer |
| 72 | + exists(Expr prev | |
| 73 | + constantAddressPointer(prev) and |
| 74 | + pointerToPointerStep(prev, pointer) |
| 75 | + ) |
| 76 | + or |
| 77 | + // lvalue -> pointer |
| 78 | + exists(Expr prev | |
| 79 | + constantAddressLValue(prev) and |
| 80 | + lvalueToPointerStep(prev, pointer) |
| 81 | + ) |
| 82 | +} |
| 83 | + |
| 84 | +/** Holds if `reference` is an _address constant expression_ of reference type. */ |
| 85 | +private predicate constantAddressReference(Expr reference) { |
| 86 | + addressConstantVariable(reference.(VariableAccess).getTarget()) and |
| 87 | + reference.getType().getUnderlyingType() instanceof ReferenceType |
| 88 | + or |
| 89 | + addressConstantVariable(reference.(VariableAccess).getTarget()) and |
| 90 | + reference.getType().getUnderlyingType() instanceof FunctionReferenceType // not a ReferenceType |
| 91 | + or |
| 92 | + // reference -> reference |
| 93 | + exists(Expr prev | |
| 94 | + constantAddressReference(prev) and |
| 95 | + referenceToReferenceStep(prev, reference) |
| 96 | + ) |
| 97 | + or |
| 98 | + // lvalue -> reference |
| 99 | + exists(Expr prev | |
| 100 | + constantAddressLValue(prev) and |
| 101 | + lvalueToReferenceStep(prev, reference) |
| 102 | + ) |
| 103 | +} |
| 104 | + |
| 105 | +private predicate lvalueToLvalueStep(Expr lvalueIn, Expr lvalueOut) { |
| 106 | + lvalueIn = lvalueOut.(DotFieldAccess).getQualifier().getFullyConverted() |
| 107 | + or |
| 108 | + lvalueIn.getConversion() = lvalueOut.(ParenthesisExpr) |
| 109 | + or |
| 110 | + // Special case for function pointers, where `fp == *fp`. |
| 111 | + lvalueIn = lvalueOut.(PointerDereferenceExpr).getOperand().getFullyConverted() and |
| 112 | + lvalueIn.getType() instanceof FunctionPointerType |
| 113 | +} |
| 114 | + |
| 115 | +private predicate pointerToLvalueStep(Expr pointerIn, Expr lvalueOut) { |
| 116 | + lvalueOut = any(ArrayExpr ae | |
| 117 | + pointerIn = ae.getArrayBase().getFullyConverted() and |
| 118 | + hasConstantValue(ae.getArrayOffset().getFullyConverted()) |
| 119 | + ) |
| 120 | + or |
| 121 | + pointerIn = lvalueOut.(PointerDereferenceExpr).getOperand().getFullyConverted() |
| 122 | + or |
| 123 | + pointerIn = lvalueOut.(PointerFieldAccess).getQualifier().getFullyConverted() |
| 124 | +} |
| 125 | + |
| 126 | +private predicate lvalueToPointerStep(Expr lvalueIn, Expr pointerOut) { |
| 127 | + lvalueIn.getConversion() = pointerOut.(ArrayToPointerConversion) |
| 128 | + or |
| 129 | + lvalueIn = pointerOut.(AddressOfExpr).getOperand().getFullyConverted() |
| 130 | +} |
| 131 | + |
| 132 | +private predicate pointerToPointerStep(Expr pointerIn, Expr pointerOut) { |
| 133 | + ( |
| 134 | + pointerOut instanceof PointerAddExpr |
| 135 | + or |
| 136 | + pointerOut instanceof PointerSubExpr |
| 137 | + ) and |
| 138 | + pointerIn = pointerOut.getAChild().getFullyConverted() and |
| 139 | + pointerIn.getType().getUnspecifiedType() instanceof PointerType and |
| 140 | + // The pointer arg won't be constant in the sense of `hasConstantValue`, so |
| 141 | + // this will have to match the integer argument. |
| 142 | + hasConstantValue(pointerOut.getAChild().getFullyConverted()) |
| 143 | + or |
| 144 | + pointerIn = pointerOut.(UnaryPlusExpr).getOperand().getFullyConverted() |
| 145 | + or |
| 146 | + pointerIn.getConversion() = pointerOut.(Cast) |
| 147 | + or |
| 148 | + pointerIn.getConversion() = pointerOut.(ParenthesisExpr) |
| 149 | + or |
| 150 | + pointerOut = any(ConditionalExpr cond | |
| 151 | + cond.getCondition().getFullyConverted().getValue().toInt() != 0 and |
| 152 | + pointerIn = cond.getThen().getFullyConverted() |
| 153 | + or |
| 154 | + cond.getCondition().getFullyConverted().getValue().toInt() = 0 and |
| 155 | + pointerIn = cond.getElse().getFullyConverted() |
| 156 | + ) |
| 157 | + or |
| 158 | + // The comma operator is allowed by C++17 but disallowed by C99. This |
| 159 | + // disjunct is a compromise that's chosen for being easy to implement. |
| 160 | + pointerOut = any(CommaExpr comma | |
| 161 | + hasConstantValue(comma.getLeftOperand()) and |
| 162 | + pointerIn = comma.getRightOperand().getFullyConverted() |
| 163 | + ) |
| 164 | +} |
| 165 | + |
| 166 | +private predicate lvalueToReferenceStep(Expr lvalueIn, Expr referenceOut) { |
| 167 | + lvalueIn.getConversion() = referenceOut.(ReferenceToExpr) |
| 168 | +} |
| 169 | + |
| 170 | +private predicate referenceToLvalueStep(Expr referenceIn, Expr lvalueOut) { |
| 171 | + // This probably cannot happen. It would require an expression to be |
| 172 | + // converted to a reference and back again without an intermediate variable |
| 173 | + // assignment. |
| 174 | + referenceIn.getConversion() = lvalueOut.(ReferenceDereferenceExpr) |
| 175 | +} |
| 176 | + |
| 177 | +private predicate referenceToReferenceStep(Expr referenceIn, Expr referenceOut) { |
| 178 | + referenceIn.getConversion() = referenceOut.(Cast) |
| 179 | + or |
| 180 | + referenceIn.getConversion() = referenceOut.(ParenthesisExpr) |
| 181 | +} |
| 182 | + |
| 183 | +/** Holds if `e` is constant according to the database. */ |
| 184 | +private predicate hasConstantValue(Expr e) { valuebind(_, underlyingElement(e)) } |
0 commit comments