Skip to content

Commit 32e04b4

Browse files
committed
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.
1 parent 1b1095e commit 32e04b4

File tree

4 files changed

+63
-22
lines changed

4 files changed

+63
-22
lines changed

cpp/ql/src/semmle/code/cpp/dataflow/EscapesTree.qll

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,13 @@ private import cpp
1919
* template functions, these functions are essentially casts, so we treat them
2020
* as such.
2121
*/
22-
private predicate stdIdentityFunction(Function f) {
23-
f.getNamespace().getParentNamespace() instanceof GlobalNamespace and
24-
f.getNamespace().getName() = "std" and
25-
(
26-
f.getName() = "move"
27-
or
28-
f.getName() = "forward"
29-
)
30-
}
22+
private predicate stdIdentityFunction(Function f) { f.hasQualifiedName("std", ["move", "forward"]) }
23+
24+
/**
25+
* Holds if `f` is an instantiation of `std::addressof`, which effectively
26+
* converts a reference to a pointer.
27+
*/
28+
private predicate stdAddressOf(Function f) { f.hasQualifiedName("std", "addressof") }
3129

3230
private predicate lvalueToLvalueStepPure(Expr lvalueIn, Expr lvalueOut) {
3331
lvalueIn = lvalueOut.(DotFieldAccess).getQualifier().getFullyConverted()
@@ -99,12 +97,17 @@ private predicate lvalueToReferenceStep(Expr lvalueIn, Expr referenceOut) {
9997
}
10098

10199
private predicate referenceToLvalueStep(Expr referenceIn, Expr lvalueOut) {
102-
// This probably cannot happen. It would require an expression to be
103-
// converted to a reference and back again without an intermediate variable
104-
// assignment.
105100
referenceIn.getConversion() = lvalueOut.(ReferenceDereferenceExpr)
106101
}
107102

103+
private predicate referenceToPointerStep(Expr referenceIn, Expr pointerOut) {
104+
pointerOut =
105+
any(FunctionCall call |
106+
stdAddressOf(call.getTarget()) and
107+
referenceIn = call.getArgument(0).getFullyConverted()
108+
)
109+
}
110+
108111
private predicate referenceToReferenceStep(Expr referenceIn, Expr referenceOut) {
109112
referenceOut =
110113
any(FunctionCall call |
@@ -153,6 +156,12 @@ private predicate pointerFromVariableAccess(VariableAccess va, Expr pointer) {
153156
pointerToPointerStep(prev, pointer)
154157
)
155158
or
159+
// reference -> pointer
160+
exists(Expr prev |
161+
referenceFromVariableAccess(va, prev) and
162+
referenceToPointerStep(prev, pointer)
163+
)
164+
or
156165
// lvalue -> pointer
157166
exists(Expr prev |
158167
lvalueFromVariableAccess(va, prev) and
@@ -177,7 +186,8 @@ private predicate referenceFromVariableAccess(VariableAccess va, Expr reference)
177186
private predicate valueMayEscapeAt(Expr e) {
178187
exists(Call call |
179188
e = call.getAnArgument().getFullyConverted() and
180-
not stdIdentityFunction(call.getTarget())
189+
not stdIdentityFunction(call.getTarget()) and
190+
not stdAddressOf(call.getTarget())
181191
)
182192
or
183193
exists(AssignExpr assign | e = assign.getRValue().getFullyConverted())

cpp/ql/src/semmle/code/cpp/dataflow/internal/AddressFlow.qll

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,13 @@ private import cpp
2121
* template functions, these functions are essentially casts, so we treat them
2222
* as such.
2323
*/
24-
private predicate stdIdentityFunction(Function f) {
25-
f.getNamespace().getParentNamespace() instanceof GlobalNamespace and
26-
f.getNamespace().getName() = "std" and
27-
(
28-
f.getName() = "move"
29-
or
30-
f.getName() = "forward"
31-
)
32-
}
24+
private predicate stdIdentityFunction(Function f) { f.hasQualifiedName("std", ["move", "forward"]) }
25+
26+
/**
27+
* Holds if `f` is an instantiation of `std::addressof`, which effectively
28+
* converts a reference to a pointer.
29+
*/
30+
private predicate stdAddressOf(Function f) { f.hasQualifiedName("std", "addressof") }
3331

3432
private predicate lvalueToLvalueStep(Expr lvalueIn, Expr lvalueOut) {
3533
lvalueIn.getConversion() = lvalueOut.(ParenthesisExpr)
@@ -96,6 +94,14 @@ private predicate referenceToLvalueStep(Expr referenceIn, Expr lvalueOut) {
9694
referenceIn.getConversion() = lvalueOut.(ReferenceDereferenceExpr)
9795
}
9896

97+
private predicate referenceToPointerStep(Expr referenceIn, Expr pointerOut) {
98+
pointerOut =
99+
any(FunctionCall call |
100+
stdAddressOf(call.getTarget()) and
101+
referenceIn = call.getArgument(0).getFullyConverted()
102+
)
103+
}
104+
99105
private predicate referenceToReferenceStep(Expr referenceIn, Expr referenceOut) {
100106
referenceOut =
101107
any(FunctionCall call |
@@ -185,6 +191,7 @@ private predicate referenceToUpdate(Expr reference, Expr outer, ControlFlowNode
185191
node = call and
186192
outer = call.getAnArgument().getFullyConverted() and
187193
not stdIdentityFunction(call.getTarget()) and
194+
not stdAddressOf(call.getTarget()) and
188195
exists(ReferenceType rt | rt = outer.getType().stripTopLevelSpecifiers() |
189196
not rt.getBaseType().isConst()
190197
)
@@ -196,6 +203,11 @@ private predicate referenceToUpdate(Expr reference, Expr outer, ControlFlowNode
196203
lvalueToUpdate(lvalueMid, outer, node)
197204
)
198205
or
206+
exists(Expr pointerMid |
207+
referenceToPointerStep(reference, pointerMid) and
208+
pointerToUpdate(pointerMid, outer, node)
209+
)
210+
or
199211
exists(Expr referenceMid |
200212
referenceToReferenceStep(reference, referenceMid) and
201213
referenceToUpdate(referenceMid, outer, node)

cpp/ql/test/library-tests/defuse/addressOf.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,3 +63,18 @@ void nonexamples(int *ptr, int &ref) {
6363
nonexamples(&*ptr, ref);
6464
}
6565
}
66+
67+
68+
namespace std {
69+
template<typename T>
70+
constexpr T *addressof(T &obj) noexcept {
71+
return __builtin_addressof(obj);
72+
}
73+
}
74+
75+
void use_std_addressof() {
76+
int x = 0;
77+
int *y = std::addressof(x) + *std::addressof(x);
78+
}
79+
80+
// semmle-extractor-options: --clang

cpp/ql/test/library-tests/defuse/isAddressOfAccess.expected

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@
2525
| addressOf.cpp:62:11:62:13 | ptr | |
2626
| addressOf.cpp:63:19:63:21 | ptr | |
2727
| addressOf.cpp:63:24:63:26 | ref | non-const address |
28+
| addressOf.cpp:71:32:71:34 | obj | |
29+
| addressOf.cpp:71:32:71:34 | obj | |
30+
| addressOf.cpp:77:27:77:27 | x | non-const address |
31+
| addressOf.cpp:77:48:77:48 | x | |
2832
| file://:0:0:0:0 | captured | |
2933
| file://:0:0:0:0 | captured | |
3034
| file://:0:0:0:0 | captured | non-const address |

0 commit comments

Comments
 (0)