Skip to content

Commit 8f6dbe9

Browse files
authored
Merge pull request #4468 from github/rdmarsh2/cpp/output-iterators-2
C++: flow through output iterators with user-defined operator= and operator*
2 parents 45cd47e + aab9797 commit 8f6dbe9

File tree

10 files changed

+669
-262
lines changed

10 files changed

+669
-262
lines changed

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

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -801,12 +801,34 @@ module FlowVar_internal {
801801
}
802802

803803
Expr getAnIteratorAccess(Variable collection) {
804-
exists(Call c, SsaDefinition def, Variable iterator |
805-
c.getQualifier() = collection.getAnAccess() and
806-
c.getTarget() instanceof BeginOrEndFunction and
804+
exists(
805+
Call c, SsaDefinition def, Variable iterator, FunctionInput input, FunctionOutput output
806+
|
807+
c.getTarget().(GetIteratorFunction).getsIterator(input, output) and
808+
(
809+
(
810+
input.isQualifierObject() or
811+
input.isQualifierAddress()
812+
) and
813+
c.getQualifier() = collection.getAnAccess()
814+
or
815+
exists(int index |
816+
input.isParameter(index) or
817+
input.isParameterDeref(index)
818+
|
819+
c.getArgument(index) = collection.getAnAccess()
820+
)
821+
) and
822+
output.isReturnValue() and
807823
def.getAnUltimateDefiningValue(iterator) = c and
808824
result = def.getAUse(iterator)
809825
)
826+
or
827+
exists(Call crement |
828+
crement = result and
829+
[crement.getQualifier(), crement.getArgument(0)] = getAnIteratorAccess(collection) and
830+
crement.getTarget().getName() = ["operator++", "operator--"]
831+
)
810832
}
811833

812834
class IteratorParameter extends Parameter {

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

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,9 @@ class IteratorPointerDereferenceOperator extends Operator, TaintFunction, Iterat
9292
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
9393
input = iteratorInput and
9494
output.isReturnValue()
95+
or
96+
input.isReturnValueDeref() and
97+
output.isParameterDeref(0)
9598
}
9699
}
97100

@@ -180,6 +183,9 @@ class IteratorPointerDereferenceMemberOperator extends MemberFunction, TaintFunc
180183
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
181184
input.isQualifierObject() and
182185
output.isReturnValue()
186+
or
187+
input.isReturnValueDeref() and
188+
output.isQualifierObject()
183189
}
184190
}
185191

@@ -274,11 +280,32 @@ class IteratorArrayMemberOperator extends MemberFunction, TaintFunction, Iterato
274280
}
275281
}
276282

283+
/**
284+
* An `operator=` member function of an iterator class that is not a copy or move assignment
285+
* operator.
286+
*
287+
* The `hasTaintFlow` override provides flow through output iterators that return themselves with
288+
* `operator*` and use their own `operator=` to assign to the container.
289+
*/
290+
class IteratorAssignmentMemberOperator extends MemberFunction, TaintFunction {
291+
IteratorAssignmentMemberOperator() {
292+
this.hasName("operator=") and
293+
this.getDeclaringType() instanceof Iterator and
294+
not this instanceof CopyAssignmentOperator and
295+
not this instanceof MoveAssignmentOperator
296+
}
297+
298+
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
299+
input.isParameterDeref(0) and
300+
output.isQualifierObject()
301+
}
302+
}
303+
277304
/**
278305
* A `begin` or `end` member function, or a related member function, that
279306
* returns an iterator.
280307
*/
281-
class BeginOrEndFunction extends MemberFunction, TaintFunction {
308+
class BeginOrEndFunction extends MemberFunction, TaintFunction, GetIteratorFunction {
282309
BeginOrEndFunction() {
283310
this
284311
.hasName(["begin", "cbegin", "rbegin", "crbegin", "end", "cend", "rend", "crend",
@@ -290,4 +317,24 @@ class BeginOrEndFunction extends MemberFunction, TaintFunction {
290317
input.isQualifierObject() and
291318
output.isReturnValue()
292319
}
320+
321+
override predicate getsIterator(FunctionInput input, FunctionOutput output) {
322+
input.isQualifierObject() and
323+
output.isReturnValue()
324+
}
325+
}
326+
327+
/**
328+
* The `std::front_inserter`, `std::inserter`, and `std::back_inserter`
329+
* functions.
330+
*/
331+
class InserterIteratorFunction extends GetIteratorFunction {
332+
InserterIteratorFunction() {
333+
this.hasQualifiedName("std", ["front_inserter", "inserter", "back_inserter"])
334+
}
335+
336+
override predicate getsIterator(FunctionInput input, FunctionOutput output) {
337+
input.isParameterDeref(0) and
338+
output.isReturnValue()
339+
}
293340
}

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,14 @@ import semmle.code.cpp.models.Models
1515
* can be used to write to the iterator's underlying collection.
1616
*/
1717
abstract class IteratorReferenceFunction extends Function { }
18+
19+
/**
20+
* A function which takes a container and returns an iterator over that container.
21+
*/
22+
abstract class GetIteratorFunction extends Function {
23+
/**
24+
* Holds if the return value or buffer represented by `output` is an iterator over the container
25+
* passd in the argument, qualifier, or buffer represented by `input`.
26+
*/
27+
abstract predicate getsIterator(FunctionInput input, FunctionOutput output);
28+
}

0 commit comments

Comments
 (0)