C++: Support std::addressof

I didn't add this support in `AddressConstantExpression.qll` since I
think it would require extra work and testing to get the constexprness
right. My long-term plan for `AddressConstantExpression.qll` is to move
its functionality to the extractor.
This commit is contained in:
Jonas Jensen
2020-05-06 11:17:13 +02:00
parent 1b1095ee75
commit 32e04b4033
4 changed files with 63 additions and 22 deletions

View File

@@ -19,15 +19,13 @@ private import cpp
* template functions, these functions are essentially casts, so we treat them * template functions, these functions are essentially casts, so we treat them
* as such. * as such.
*/ */
private predicate stdIdentityFunction(Function f) { private predicate stdIdentityFunction(Function f) { f.hasQualifiedName("std", ["move", "forward"]) }
f.getNamespace().getParentNamespace() instanceof GlobalNamespace and
f.getNamespace().getName() = "std" and /**
( * Holds if `f` is an instantiation of `std::addressof`, which effectively
f.getName() = "move" * converts a reference to a pointer.
or */
f.getName() = "forward" private predicate stdAddressOf(Function f) { f.hasQualifiedName("std", "addressof") }
)
}
private predicate lvalueToLvalueStepPure(Expr lvalueIn, Expr lvalueOut) { private predicate lvalueToLvalueStepPure(Expr lvalueIn, Expr lvalueOut) {
lvalueIn = lvalueOut.(DotFieldAccess).getQualifier().getFullyConverted() lvalueIn = lvalueOut.(DotFieldAccess).getQualifier().getFullyConverted()
@@ -99,12 +97,17 @@ private predicate lvalueToReferenceStep(Expr lvalueIn, Expr referenceOut) {
} }
private predicate referenceToLvalueStep(Expr referenceIn, Expr lvalueOut) { private predicate referenceToLvalueStep(Expr referenceIn, Expr lvalueOut) {
// This probably cannot happen. It would require an expression to be
// converted to a reference and back again without an intermediate variable
// assignment.
referenceIn.getConversion() = lvalueOut.(ReferenceDereferenceExpr) referenceIn.getConversion() = lvalueOut.(ReferenceDereferenceExpr)
} }
private predicate referenceToPointerStep(Expr referenceIn, Expr pointerOut) {
pointerOut =
any(FunctionCall call |
stdAddressOf(call.getTarget()) and
referenceIn = call.getArgument(0).getFullyConverted()
)
}
private predicate referenceToReferenceStep(Expr referenceIn, Expr referenceOut) { private predicate referenceToReferenceStep(Expr referenceIn, Expr referenceOut) {
referenceOut = referenceOut =
any(FunctionCall call | any(FunctionCall call |
@@ -153,6 +156,12 @@ private predicate pointerFromVariableAccess(VariableAccess va, Expr pointer) {
pointerToPointerStep(prev, pointer) pointerToPointerStep(prev, pointer)
) )
or or
// reference -> pointer
exists(Expr prev |
referenceFromVariableAccess(va, prev) and
referenceToPointerStep(prev, pointer)
)
or
// lvalue -> pointer // lvalue -> pointer
exists(Expr prev | exists(Expr prev |
lvalueFromVariableAccess(va, prev) and lvalueFromVariableAccess(va, prev) and
@@ -177,7 +186,8 @@ private predicate referenceFromVariableAccess(VariableAccess va, Expr reference)
private predicate valueMayEscapeAt(Expr e) { private predicate valueMayEscapeAt(Expr e) {
exists(Call call | exists(Call call |
e = call.getAnArgument().getFullyConverted() and e = call.getAnArgument().getFullyConverted() and
not stdIdentityFunction(call.getTarget()) not stdIdentityFunction(call.getTarget()) and
not stdAddressOf(call.getTarget())
) )
or or
exists(AssignExpr assign | e = assign.getRValue().getFullyConverted()) exists(AssignExpr assign | e = assign.getRValue().getFullyConverted())

View File

@@ -21,15 +21,13 @@ private import cpp
* template functions, these functions are essentially casts, so we treat them * template functions, these functions are essentially casts, so we treat them
* as such. * as such.
*/ */
private predicate stdIdentityFunction(Function f) { private predicate stdIdentityFunction(Function f) { f.hasQualifiedName("std", ["move", "forward"]) }
f.getNamespace().getParentNamespace() instanceof GlobalNamespace and
f.getNamespace().getName() = "std" and /**
( * Holds if `f` is an instantiation of `std::addressof`, which effectively
f.getName() = "move" * converts a reference to a pointer.
or */
f.getName() = "forward" private predicate stdAddressOf(Function f) { f.hasQualifiedName("std", "addressof") }
)
}
private predicate lvalueToLvalueStep(Expr lvalueIn, Expr lvalueOut) { private predicate lvalueToLvalueStep(Expr lvalueIn, Expr lvalueOut) {
lvalueIn.getConversion() = lvalueOut.(ParenthesisExpr) lvalueIn.getConversion() = lvalueOut.(ParenthesisExpr)
@@ -96,6 +94,14 @@ private predicate referenceToLvalueStep(Expr referenceIn, Expr lvalueOut) {
referenceIn.getConversion() = lvalueOut.(ReferenceDereferenceExpr) referenceIn.getConversion() = lvalueOut.(ReferenceDereferenceExpr)
} }
private predicate referenceToPointerStep(Expr referenceIn, Expr pointerOut) {
pointerOut =
any(FunctionCall call |
stdAddressOf(call.getTarget()) and
referenceIn = call.getArgument(0).getFullyConverted()
)
}
private predicate referenceToReferenceStep(Expr referenceIn, Expr referenceOut) { private predicate referenceToReferenceStep(Expr referenceIn, Expr referenceOut) {
referenceOut = referenceOut =
any(FunctionCall call | any(FunctionCall call |
@@ -185,6 +191,7 @@ private predicate referenceToUpdate(Expr reference, Expr outer, ControlFlowNode
node = call and node = call and
outer = call.getAnArgument().getFullyConverted() and outer = call.getAnArgument().getFullyConverted() and
not stdIdentityFunction(call.getTarget()) and not stdIdentityFunction(call.getTarget()) and
not stdAddressOf(call.getTarget()) and
exists(ReferenceType rt | rt = outer.getType().stripTopLevelSpecifiers() | exists(ReferenceType rt | rt = outer.getType().stripTopLevelSpecifiers() |
not rt.getBaseType().isConst() not rt.getBaseType().isConst()
) )
@@ -196,6 +203,11 @@ private predicate referenceToUpdate(Expr reference, Expr outer, ControlFlowNode
lvalueToUpdate(lvalueMid, outer, node) lvalueToUpdate(lvalueMid, outer, node)
) )
or or
exists(Expr pointerMid |
referenceToPointerStep(reference, pointerMid) and
pointerToUpdate(pointerMid, outer, node)
)
or
exists(Expr referenceMid | exists(Expr referenceMid |
referenceToReferenceStep(reference, referenceMid) and referenceToReferenceStep(reference, referenceMid) and
referenceToUpdate(referenceMid, outer, node) referenceToUpdate(referenceMid, outer, node)

View File

@@ -63,3 +63,18 @@ void nonexamples(int *ptr, int &ref) {
nonexamples(&*ptr, ref); nonexamples(&*ptr, ref);
} }
} }
namespace std {
template<typename T>
constexpr T *addressof(T &obj) noexcept {
return __builtin_addressof(obj);
}
}
void use_std_addressof() {
int x = 0;
int *y = std::addressof(x) + *std::addressof(x);
}
// semmle-extractor-options: --clang

View File

@@ -25,6 +25,10 @@
| addressOf.cpp:62:11:62:13 | ptr | | | addressOf.cpp:62:11:62:13 | ptr | |
| addressOf.cpp:63:19:63:21 | ptr | | | addressOf.cpp:63:19:63:21 | ptr | |
| addressOf.cpp:63:24:63:26 | ref | non-const address | | addressOf.cpp:63:24:63:26 | ref | non-const address |
| addressOf.cpp:71:32:71:34 | obj | |
| addressOf.cpp:71:32:71:34 | obj | |
| addressOf.cpp:77:27:77:27 | x | non-const address |
| addressOf.cpp:77:48:77:48 | x | |
| file://:0:0:0:0 | captured | | | file://:0:0:0:0 | captured | |
| file://:0:0:0:0 | captured | | | file://:0:0:0:0 | captured | |
| file://:0:0:0:0 | captured | non-const address | | file://:0:0:0:0 | captured | non-const address |