Skip to content

Commit f94ca0e

Browse files
committed
C++: Add implicit defs and uses for iterators' underlying containers.
1 parent 78b7e12 commit f94ca0e

3 files changed

Lines changed: 195 additions & 2 deletions

File tree

cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaInternals.qll

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,17 @@ private newtype TDefOrUseImpl =
119119
TUseImpl(Operand operand, int indirectionIndex) {
120120
isUse(_, operand, _, _, indirectionIndex) and
121121
not isDef(_, _, operand, _, _, _)
122+
} or
123+
TIteratorDef(
124+
Operand iteratorAddress, BaseSourceVariableInstruction container, int indirectionIndex
125+
) {
126+
isIteratorDef(container, iteratorAddress, _, _, indirectionIndex) and
127+
any(SsaInternals0::Def def | def.isIteratorDef()).getAddressOperand() = iteratorAddress
128+
} or
129+
TIteratorUse(
130+
Operand iteratorAddress, BaseSourceVariableInstruction container, int indirectionIndex
131+
) {
132+
isIteratorUse(container, iteratorAddress, _, indirectionIndex)
122133
}
123134

124135
abstract private class DefOrUseImpl extends TDefOrUseImpl {
@@ -230,8 +241,20 @@ private class DirectDef extends DefImpl, TDefImpl {
230241
override Node0Impl getValue() { isDef(_, result, address, _, _, _) }
231242

232243
override predicate isCertain() { isDef(true, _, address, _, _, ind) }
244+
}
245+
246+
private class IteratorDef extends DefImpl, TIteratorDef {
247+
BaseSourceVariableInstruction container;
248+
249+
IteratorDef() { this = TIteratorDef(address, container, ind) }
250+
251+
override BaseSourceVariableInstruction getBase() { result = container }
252+
253+
override int getIndirection() { isIteratorDef(container, address, _, result, ind) }
233254

234-
predicate isCertain() { isDef(true, _, address, _, _, ind) }
255+
override Node0Impl getValue() { isIteratorDef(container, address, result, _, _) }
256+
257+
override predicate isCertain() { none() }
235258
}
236259

237260
abstract class UseImpl extends DefOrUseImpl {
@@ -270,6 +293,18 @@ private class DirectUse extends UseImpl, TUseImpl {
270293
override predicate isCertain() { isUse(true, operand, _, _, ind) }
271294
}
272295

296+
private class IteratorUse extends UseImpl, TIteratorUse {
297+
BaseSourceVariableInstruction container;
298+
299+
IteratorUse() { this = TIteratorUse(operand, container, ind) }
300+
301+
override int getIndirection() { isIteratorUse(container, operand, result, ind) }
302+
303+
override BaseSourceVariableInstruction getBase() { result = container }
304+
305+
override predicate isCertain() { none() }
306+
}
307+
273308
/**
274309
* Holds if `defOrUse1` is a definition which is first read by `use`,
275310
* or if `defOrUse1` is a use and `use` is a next subsequent use.

cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaInternalsCommon.qll

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,136 @@ private class BaseAllocationInstruction extends BaseSourceVariableInstruction, A
410410

411411
cached
412412
private module Cached {
413+
private import semmle.code.cpp.models.interfaces.Iterator as Interfaces
414+
private import semmle.code.cpp.models.implementations.Iterator as Iterator
415+
416+
/**
417+
* Holds if `next` is a instruction with a memory result that potentially
418+
* updates the memory produced by `prev`.
419+
*/
420+
private predicate memorySucc(Instruction prev, Instruction next) {
421+
prev = next.(ChiInstruction).getTotal()
422+
or
423+
// Phi inputs can be inexact.
424+
prev = next.(PhiInstruction).getAnInputOperand().getAnyDef()
425+
or
426+
prev = next.(CopyInstruction).getSourceValue()
427+
or
428+
exists(ReadSideEffectInstruction read |
429+
next = read.getPrimaryInstruction() and
430+
isAdditionalConversionFlow(_, next) and
431+
prev = read.getSideEffectOperand().getAnyDef()
432+
)
433+
}
434+
435+
/**
436+
* Holds if `iteratorDerefAddress` is an address of an iterator dereference (i.e., `*it`)
437+
* that is used for a write operation that writes the value `value`. The `memory` instruction
438+
* represents the memory that the IR's SSA analysis determined was read by the call to `operator*`.
439+
*
440+
* The `numberOfLoads` integer represents the number of dereferences this write corresponds to
441+
* on the underlying container that produced the iterator.
442+
*/
443+
private predicate isChiAfterIteratorDef(
444+
Instruction memory, Operand iteratorDerefAddress, Node0Impl value, int numberOfLoads
445+
) {
446+
exists(
447+
BaseSourceVariableInstruction iteratorBase, ReadSideEffectInstruction read,
448+
Operand iteratorAddress
449+
|
450+
numberOfLoads >= 0 and
451+
isDef(_, value, iteratorDerefAddress, iteratorBase, numberOfLoads + 2, 0) and
452+
isUse(_, iteratorAddress, iteratorBase, numberOfLoads + 1, 0) and
453+
iteratorBase.getResultType() instanceof Interfaces::Iterator and
454+
isDereference(iteratorAddress.getDef(), read.getArgumentDef().getAUse()) and
455+
memory = read.getSideEffectOperand().getAnyDef()
456+
)
457+
}
458+
459+
/**
460+
* Holds if `iterator` is a `StoreInstruction` that stores the result of some function
461+
* returning an iterator into an address computed started at `containerBase`.
462+
*
463+
* For example, given a declaration like `std::vector<int>::iterator it = v.begin()`,
464+
* the `iterator` will be the `StoreInstruction` generated by the write to `it`, and
465+
* `containerBase` will be the address of `v`.
466+
*/
467+
private predicate isChiAfterBegin(
468+
BaseSourceVariableInstruction containerBase, StoreInstruction iterator
469+
) {
470+
exists(CallInstruction getIterator |
471+
getIterator = iterator.getSourceValue() and
472+
getIterator.getStaticCallTarget() instanceof Iterator::GetIteratorFunction and
473+
isDef(_, any(Node0Impl n | n.asInstruction() = iterator), _, _, 1, 0) and
474+
isUse(_, getIterator.getThisArgumentOperand(), containerBase, 0, 0)
475+
)
476+
}
477+
478+
/**
479+
* Holds if `iteratorDerefAddress` is an address of an iterator dereference (i.e., `*it`)
480+
* that is used for a read operation. The `memory` instruction represents the memory that
481+
* the IR's SSA analysis determined was read by the call to `operator*`.
482+
*
483+
* Finally, the `numberOfLoads` integer represents the number of dereferences this read
484+
* corresponds to on the underlying container that produced the iterator.
485+
*/
486+
private predicate isChiBeforeIteratorUse(
487+
Operand iteratorDerefAddress, Instruction memory, int numberOfLoads
488+
) {
489+
exists(
490+
BaseSourceVariableInstruction iteratorBase, LoadInstruction load,
491+
ReadSideEffectInstruction read
492+
|
493+
numberOfLoads >= 0 and
494+
isUse(_, iteratorDerefAddress, iteratorBase, numberOfLoads + 2, 0) and
495+
iteratorBase.getResultType() instanceof Interfaces::Iterator and
496+
read.getPrimaryInstruction() = load.getSourceAddress() and
497+
memory = read.getSideEffectOperand().getAnyDef()
498+
)
499+
}
500+
501+
/**
502+
* Holds if `iteratorDerefAddress` is an address of an iterator dereference (i.e., `*it`)
503+
* that is used for a write operation that writes the value `value` to a container that
504+
* created the iterator. `container` represents the base of the address of the container
505+
* that was used to create the iterator.
506+
*/
507+
cached
508+
predicate isIteratorDef(
509+
BaseSourceVariableInstruction container, Operand iteratorDerefAddress, Node0Impl value,
510+
int numberOfLoads, int indirectionIndex
511+
) {
512+
exists(Instruction memory, Instruction begin, int upper, int ind |
513+
isChiAfterIteratorDef(memory, iteratorDerefAddress, value, numberOfLoads) and
514+
memorySucc*(begin, memory) and
515+
isChiAfterBegin(container, begin) and
516+
upper = countIndirectionsForCppType(container.getResultLanguageType()) and
517+
ind = numberOfLoads + [1 .. upper] and
518+
indirectionIndex = ind - (numberOfLoads + 1)
519+
)
520+
}
521+
522+
/**
523+
* Holds if `iteratorDerefAddress` is an address of an iterator dereference (i.e., `*it`)
524+
* that is used for a read operation to read a value from a container that created the iterator.
525+
* `container` represents the base of the address of the container that was used to create
526+
* the iterator.
527+
*/
528+
cached
529+
predicate isIteratorUse(
530+
BaseSourceVariableInstruction container, Operand iteratorDerefAddress, int numberOfLoads,
531+
int indirectionIndex
532+
) {
533+
exists(Instruction begin, Instruction memory, int upper, int ind |
534+
isChiBeforeIteratorUse(iteratorDerefAddress, memory, numberOfLoads) and
535+
memorySucc*(begin, memory) and
536+
isChiAfterBegin(container, begin) and
537+
upper = countIndirectionsForCppType(container.getResultLanguageType()) and
538+
ind = numberOfLoads + [1 .. upper] and
539+
indirectionIndex = ind - (numberOfLoads + 1)
540+
)
541+
}
542+
413543
/**
414544
* Holds if `op` is a use of an SSA variable rooted at `base` with `ind` number
415545
* of indirections.

cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/ssa0/SsaInternals.qll

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,12 @@ private newtype TDefOrUseImpl =
3333
TUseImpl(Operand operand) {
3434
isUse(_, operand, _, _, _) and
3535
not isDef(true, _, operand, _, _, _)
36+
} or
37+
TIteratorDef(BaseSourceVariableInstruction container, Operand iteratorAddress) {
38+
isIteratorDef(container, iteratorAddress, _, _, _)
39+
} or
40+
TIteratorUse(BaseSourceVariableInstruction container, Operand iteratorAddress) {
41+
isIteratorUse(container, iteratorAddress, _, _)
3642
}
3743

3844
abstract private class DefOrUseImpl extends TDefOrUseImpl {
@@ -93,8 +99,18 @@ private class DirectDef extends DefImpl, TDefImpl {
9399
override Node0Impl getValue() { isDef(_, result, address, _, _, _) }
94100

95101
override predicate isCertain() { isDef(true, _, address, _, _, _) }
102+
}
103+
104+
private class IteratorDef extends DefImpl, TIteratorDef {
105+
BaseSourceVariableInstruction container;
106+
107+
IteratorDef() { this = TIteratorDef(container, address) }
108+
109+
override BaseSourceVariableInstruction getBase() { result = container }
110+
111+
override Node0Impl getValue() { isIteratorDef(_, address, result, _, _) }
96112

97-
predicate isCertain() { isDef(true, _, address, _, _, _) }
113+
override predicate isCertain() { none() }
98114
}
99115

100116
abstract class UseImpl extends DefOrUseImpl {
@@ -121,6 +137,16 @@ private class DirectUse extends UseImpl, TUseImpl {
121137
override predicate isCertain() { isUse(true, operand, _, _, _) }
122138
}
123139

140+
private class IteratorUse extends UseImpl, TIteratorUse {
141+
BaseSourceVariableInstruction container;
142+
143+
IteratorUse() { this = TIteratorUse(container, operand) }
144+
145+
override BaseSourceVariableInstruction getBase() { result = container }
146+
147+
override predicate isCertain() { none() }
148+
}
149+
124150
private module SsaInput implements SsaImplCommon::InputSig {
125151
import InputSigCommon
126152
import SourceVariables
@@ -223,6 +249,8 @@ class Def extends DefOrUse {
223249
override string toString() { result = this.asDefOrUse().toString() }
224250

225251
BaseSourceVariableInstruction getBase() { result = defOrUse.getBase() }
252+
253+
predicate isIteratorDef() { defOrUse instanceof IteratorDef }
226254
}
227255

228256
private module SsaImpl = SsaImplCommon::Make<SsaInput>;

0 commit comments

Comments
 (0)