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
* as such.
*/
private predicate stdIdentityFunction(Function f) {
f.getNamespace().getParentNamespace() instanceof GlobalNamespace and
f.getNamespace().getName() = "std" and
(
f.getName() = "move"
or
f.getName() = "forward"
)
}
private predicate stdIdentityFunction(Function f) { f.hasQualifiedName("std", ["move", "forward"]) }
/**
* Holds if `f` is an instantiation of `std::addressof`, which effectively
* converts a reference to a pointer.
*/
private predicate stdAddressOf(Function f) { f.hasQualifiedName("std", "addressof") }
private predicate lvalueToLvalueStepPure(Expr lvalueIn, Expr lvalueOut) {
lvalueIn = lvalueOut.(DotFieldAccess).getQualifier().getFullyConverted()
@@ -99,12 +97,17 @@ private predicate lvalueToReferenceStep(Expr lvalueIn, Expr referenceOut) {
}
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)
}
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) {
referenceOut =
any(FunctionCall call |
@@ -153,6 +156,12 @@ private predicate pointerFromVariableAccess(VariableAccess va, Expr pointer) {
pointerToPointerStep(prev, pointer)
)
or
// reference -> pointer
exists(Expr prev |
referenceFromVariableAccess(va, prev) and
referenceToPointerStep(prev, pointer)
)
or
// lvalue -> pointer
exists(Expr prev |
lvalueFromVariableAccess(va, prev) and
@@ -177,7 +186,8 @@ private predicate referenceFromVariableAccess(VariableAccess va, Expr reference)
private predicate valueMayEscapeAt(Expr e) {
exists(Call call |
e = call.getAnArgument().getFullyConverted() and
not stdIdentityFunction(call.getTarget())
not stdIdentityFunction(call.getTarget()) and
not stdAddressOf(call.getTarget())
)
or
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
* as such.
*/
private predicate stdIdentityFunction(Function f) {
f.getNamespace().getParentNamespace() instanceof GlobalNamespace and
f.getNamespace().getName() = "std" and
(
f.getName() = "move"
or
f.getName() = "forward"
)
}
private predicate stdIdentityFunction(Function f) { f.hasQualifiedName("std", ["move", "forward"]) }
/**
* Holds if `f` is an instantiation of `std::addressof`, which effectively
* converts a reference to a pointer.
*/
private predicate stdAddressOf(Function f) { f.hasQualifiedName("std", "addressof") }
private predicate lvalueToLvalueStep(Expr lvalueIn, Expr lvalueOut) {
lvalueIn.getConversion() = lvalueOut.(ParenthesisExpr)
@@ -96,6 +94,14 @@ private predicate referenceToLvalueStep(Expr referenceIn, Expr lvalueOut) {
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) {
referenceOut =
any(FunctionCall call |
@@ -185,6 +191,7 @@ private predicate referenceToUpdate(Expr reference, Expr outer, ControlFlowNode
node = call and
outer = call.getAnArgument().getFullyConverted() and
not stdIdentityFunction(call.getTarget()) and
not stdAddressOf(call.getTarget()) and
exists(ReferenceType rt | rt = outer.getType().stripTopLevelSpecifiers() |
not rt.getBaseType().isConst()
)
@@ -196,6 +203,11 @@ private predicate referenceToUpdate(Expr reference, Expr outer, ControlFlowNode
lvalueToUpdate(lvalueMid, outer, node)
)
or
exists(Expr pointerMid |
referenceToPointerStep(reference, pointerMid) and
pointerToUpdate(pointerMid, outer, node)
)
or
exists(Expr referenceMid |
referenceToReferenceStep(reference, referenceMid) and
referenceToUpdate(referenceMid, outer, node)

View File

@@ -63,3 +63,18 @@ void nonexamples(int *ptr, int &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:63:19:63:21 | ptr | |
| 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 | non-const address |