Skip to content

Commit b4f01c9

Browse files
authored
Merge pull request github#5578 from MathiasVP/ast-flow-smart-pointers
C++: AST dataflow through smart pointers
2 parents 447f339 + bc7cc2f commit b4f01c9

File tree

10 files changed

+417
-193
lines changed

10 files changed

+417
-193
lines changed

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

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616

1717
private import cpp
18+
private import semmle.code.cpp.models.interfaces.PointerWrapper
1819

1920
/**
2021
* Holds if `f` is an instantiation of the `std::move` or `std::forward`
@@ -94,6 +95,12 @@ private predicate pointerToPointerStep(Expr pointerIn, Expr pointerOut) {
9495

9596
private predicate lvalueToReferenceStep(Expr lvalueIn, Expr referenceOut) {
9697
lvalueIn.getConversion() = referenceOut.(ReferenceToExpr)
98+
or
99+
exists(PointerWrapper wrapper, Call call | call = referenceOut |
100+
referenceOut.getUnspecifiedType() instanceof ReferenceType and
101+
call = wrapper.getAnUnwrapperFunction().getACallToThisFunction() and
102+
lvalueIn = call.getQualifier().getFullyConverted()
103+
)
97104
}
98105

99106
private predicate referenceToLvalueStep(Expr referenceIn, Expr lvalueOut) {
@@ -106,6 +113,13 @@ private predicate referenceToPointerStep(Expr referenceIn, Expr pointerOut) {
106113
stdAddressOf(call.getTarget()) and
107114
referenceIn = call.getArgument(0).getFullyConverted()
108115
)
116+
or
117+
exists(CopyConstructor copy, Call call | call = pointerOut |
118+
copy.getDeclaringType() instanceof PointerWrapper and
119+
call.getTarget() = copy and
120+
// The 0'th argument is the value being copied.
121+
referenceIn = call.getArgument(0).getFullyConverted()
122+
)
109123
}
110124

111125
private predicate referenceToReferenceStep(Expr referenceIn, Expr referenceOut) {
@@ -190,6 +204,19 @@ private predicate pointerToUpdate(Expr pointer, Expr outer, ControlFlowNode node
190204
// See the `lvalueToUpdate` case for an explanation of this conjunct.
191205
call.getType().isDeeplyConstBelow()
192206
)
207+
or
208+
// Pointer wrappers behave as raw pointers for dataflow purposes.
209+
outer = call.getAnArgument().getFullyConverted() and
210+
exists(PointerWrapper wrapper | wrapper = outer.getType().stripTopLevelSpecifiers() |
211+
not wrapper.pointsToConst()
212+
)
213+
or
214+
outer = call.getQualifier().getFullyConverted() and
215+
outer.getUnspecifiedType() instanceof PointerWrapper and
216+
not (
217+
call.getTarget().hasSpecifier("const") and
218+
call.getType().isDeeplyConstBelow()
219+
)
193220
)
194221
or
195222
exists(PointerFieldAccess fa |
@@ -218,7 +245,9 @@ private predicate referenceToUpdate(Expr reference, Expr outer, ControlFlowNode
218245
not stdIdentityFunction(call.getTarget()) and
219246
not stdAddressOf(call.getTarget()) and
220247
exists(ReferenceType rt | rt = outer.getType().stripTopLevelSpecifiers() |
221-
not rt.getBaseType().isConst()
248+
not rt.getBaseType().isConst() or
249+
rt.getBaseType().getUnspecifiedType() =
250+
any(PointerWrapper wrapper | not wrapper.pointsToConst())
222251
)
223252
) and
224253
reference = outer

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,7 @@ private class VariablePartialDefinitionNode extends PartialDefinitionNode {
324324
* A synthetic data flow node used for flow into a collection when an iterator
325325
* write occurs in a callee.
326326
*/
327-
class IteratorPartialDefinitionNode extends PartialDefinitionNode {
327+
private class IteratorPartialDefinitionNode extends PartialDefinitionNode {
328328
override IteratorPartialDefinition pd;
329329

330330
override Node getPreUpdateNode() { pd.definesExpressions(_, result.asExpr()) }

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

Lines changed: 34 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ private import semmle.code.cpp.controlflow.SSA
77
private import semmle.code.cpp.dataflow.internal.SubBasicBlocks
88
private import semmle.code.cpp.dataflow.internal.AddressFlow
99
private import semmle.code.cpp.models.implementations.Iterator
10+
private import semmle.code.cpp.models.interfaces.PointerWrapper
1011

1112
/**
1213
* A conceptual variable that is assigned only once, like an SSA variable. This
@@ -158,18 +159,14 @@ private module PartialDefinitions {
158159
Expr innerDefinedExpr;
159160

160161
IteratorPartialDefinition() {
161-
exists(Expr convertedInner |
162-
not this instanceof Conversion and
163-
valueToUpdate(convertedInner, this.getFullyConverted(), node) and
164-
innerDefinedExpr = convertedInner.getUnconverted() and
165-
(
166-
innerDefinedExpr.(Call).getQualifier() = getAnIteratorAccess(collection)
167-
or
168-
innerDefinedExpr.(Call).getQualifier() = collection.getAnAccess() and
169-
collection instanceof IteratorParameter
170-
) and
171-
innerDefinedExpr.(Call).getTarget() instanceof IteratorPointerDereferenceMemberOperator
172-
)
162+
innerDefinedExpr = getInnerDefinedExpr(this, node) and
163+
(
164+
innerDefinedExpr.(Call).getQualifier() = getAnIteratorAccess(collection)
165+
or
166+
innerDefinedExpr.(Call).getQualifier() = collection.getAnAccess() and
167+
collection instanceof IteratorParameter
168+
) and
169+
innerDefinedExpr.(Call).getTarget() instanceof IteratorPointerDereferenceMemberOperator
173170
or
174171
// iterators passed by value without a copy constructor
175172
exists(Call call |
@@ -207,16 +204,18 @@ private module PartialDefinitions {
207204
}
208205
}
209206

207+
private Expr getInnerDefinedExpr(Expr e, ControlFlowNode node) {
208+
not e instanceof Conversion and
209+
exists(Expr convertedInner |
210+
valueToUpdate(convertedInner, e.getFullyConverted(), node) and
211+
result = convertedInner.getUnconverted()
212+
)
213+
}
214+
210215
class VariablePartialDefinition extends PartialDefinition {
211216
Expr innerDefinedExpr;
212217

213-
VariablePartialDefinition() {
214-
not this instanceof Conversion and
215-
exists(Expr convertedInner |
216-
valueToUpdate(convertedInner, this.getFullyConverted(), node) and
217-
innerDefinedExpr = convertedInner.getUnconverted()
218-
)
219-
}
218+
VariablePartialDefinition() { innerDefinedExpr = getInnerDefinedExpr(this, node) }
220219

221220
deprecated override predicate partiallyDefines(Variable v) {
222221
innerDefinedExpr = v.getAnAccess()
@@ -296,7 +295,8 @@ module FlowVar_internal {
296295
// treating them as immutable, but for data flow it gives better results in
297296
// practice to make the variable synonymous with its contents.
298297
not v.getUnspecifiedType() instanceof ReferenceType and
299-
not v instanceof IteratorParameter
298+
not v instanceof IteratorParameter and
299+
not v instanceof PointerWrapperParameter
300300
}
301301

302302
/**
@@ -644,10 +644,19 @@ module FlowVar_internal {
644644
predicate parameterIsNonConstReference(Parameter p) {
645645
exists(ReferenceType refType |
646646
refType = p.getUnderlyingType() and
647-
not refType.getBaseType().isConst()
647+
(
648+
not refType.getBaseType().isConst()
649+
or
650+
// A field of a parameter of type `const std::shared_ptr<A>& p` can still be changed even though
651+
// the base type of the reference is `const`.
652+
refType.getBaseType().getUnspecifiedType() =
653+
any(PointerWrapper wrapper | not wrapper.pointsToConst())
654+
)
648655
)
649656
or
650657
p instanceof IteratorParameter
658+
or
659+
p instanceof PointerWrapperParameter
651660
}
652661

653662
/**
@@ -836,6 +845,10 @@ module FlowVar_internal {
836845
IteratorParameter() { this.getUnspecifiedType() instanceof Iterator }
837846
}
838847

848+
class PointerWrapperParameter extends Parameter {
849+
PointerWrapperParameter() { this.getUnspecifiedType() instanceof PointerWrapper }
850+
}
851+
839852
/**
840853
* Holds if `v` is initialized to have value `assignedExpr`.
841854
*/

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
private import semmle.code.cpp.models.interfaces.DataFlow
1212
private import semmle.code.cpp.models.interfaces.Taint
1313
private import semmle.code.cpp.models.interfaces.Iterator
14+
private import semmle.code.cpp.models.interfaces.PointerWrapper
1415

1516
private module DataFlow {
1617
import semmle.code.cpp.dataflow.internal.DataFlowUtil
@@ -141,7 +142,10 @@ private predicate noFlowFromChildExpr(Expr e) {
141142
or
142143
e instanceof LogicalOrExpr
143144
or
144-
e instanceof Call
145+
// Allow taint from `operator*` on smart pointers.
146+
exists(Call call | e = call |
147+
not call.getTarget() = any(PointerWrapper wrapper).getAnUnwrapperFunction()
148+
)
145149
or
146150
e instanceof SizeofOperator
147151
or

cpp/ql/src/semmle/code/cpp/exprs/Call.qll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,7 @@ class OverloadedPointerDereferenceFunction extends Function {
314314
* T1 operator*(const T2 &);
315315
* T1 a; T2 b;
316316
* a = *b;
317+
* ```
317318
*/
318319
class OverloadedPointerDereferenceExpr extends FunctionCall {
319320
OverloadedPointerDereferenceExpr() {

cpp/ql/src/semmle/code/cpp/models/implementations/SmartPointer.qll

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ private class UniqueOrSharedPtr extends Class, PointerWrapper {
1313
or
1414
result.getClassAndName(["operator->", "get"]) = this
1515
}
16+
17+
override predicate pointsToConst() { this.getTemplateArgument(0).(Type).isConst() }
1618
}
1719

1820
/** Any function that unwraps a pointer wrapper class to reveal the underlying pointer. */

cpp/ql/src/semmle/code/cpp/models/interfaces/PointerWrapper.qll

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,7 @@ abstract class PointerWrapper extends Class {
1111
* that return a reference to the pointed-to object.
1212
*/
1313
abstract MemberFunction getAnUnwrapperFunction();
14+
15+
/** Holds if the type of the data that is pointed to by this pointer wrapper is `const`. */
16+
abstract predicate pointsToConst();
1417
}

0 commit comments

Comments
 (0)