Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 88338bd

Browse files
committed
C++: Flow out of functions that write to iterators.
1 parent 41ea71c commit 88338bd

3 files changed

Lines changed: 121 additions & 25 deletions

File tree

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -381,7 +381,7 @@ private Operand fullyConvertedCallStep(Operand op) {
381381
*/
382382
private Instruction getUse(Operand op) {
383383
result = op.getUse() and
384-
not Ssa::ignoreOperand(op)
384+
not Ssa::ignoreInstruction(result)
385385
}
386386

387387
/** Gets a use of the instruction `instr` that is not ignored for dataflow purposes. */

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

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -121,10 +121,10 @@ private newtype TDefOrUseImpl =
121121
not isDef(_, _, operand, _, _, _)
122122
} or
123123
TIteratorDef(
124-
Operand iteratorAddress, BaseSourceVariableInstruction container, int indirectionIndex
124+
Operand iteratorDerefAddress, BaseSourceVariableInstruction container, int indirectionIndex
125125
) {
126-
isIteratorDef(container, iteratorAddress, _, _, indirectionIndex) and
127-
any(SsaInternals0::Def def | def.isIteratorDef()).getAddressOperand() = iteratorAddress
126+
isIteratorDef(container, iteratorDerefAddress, _, _, indirectionIndex) and
127+
any(SsaInternals0::Def def | def.isIteratorDef()).getAddressOperand() = iteratorDerefAddress
128128
} or
129129
TIteratorUse(
130130
Operand iteratorAddress, BaseSourceVariableInstruction container, int indirectionIndex
@@ -465,11 +465,20 @@ private predicate indirectConversionFlowStep(Node nFrom, Node nTo) {
465465
nodeToDefOrUse(nTo, defOrUse, _) and
466466
adjacentDefRead(defOrUse, _)
467467
) and
468-
exists(Operand op1, Operand op2, int indirectionIndex, Instruction instr |
469-
hasOperandAndIndex(nFrom, op1, pragma[only_bind_into](indirectionIndex)) and
470-
hasOperandAndIndex(nTo, op2, pragma[only_bind_into](indirectionIndex)) and
471-
instr = op2.getDef() and
472-
conversionFlow(op1, instr, _, _)
468+
(
469+
exists(Operand op1, Operand op2, int indirectionIndex, Instruction instr |
470+
hasOperandAndIndex(nFrom, op1, pragma[only_bind_into](indirectionIndex)) and
471+
hasOperandAndIndex(nTo, op2, pragma[only_bind_into](indirectionIndex)) and
472+
instr = op2.getDef() and
473+
conversionFlow(op1, instr, _, _)
474+
)
475+
or
476+
exists(Operand op1, Operand op2, int indirectionIndex, Instruction instr |
477+
hasOperandAndIndex(nFrom, op1, pragma[only_bind_into](indirectionIndex)) and
478+
hasOperandAndIndex(nTo, op2, indirectionIndex - 1) and
479+
instr = op2.getDef() and
480+
isDereference(instr, op1)
481+
)
473482
)
474483
}
475484

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

Lines changed: 103 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -406,7 +406,10 @@ private predicate isModifiableAtImpl(CppType cppType, int indirectionIndex) {
406406
(
407407
exists(Type pointerType, Type base, Type t |
408408
pointerType = t.getUnderlyingType() and
409-
(pointerType instanceof PointerOrReferenceType or pointerType instanceof Cpp::ArrayType) and
409+
(
410+
pointerType = any(Indirection ind).getUnderlyingType() or
411+
pointerType instanceof Cpp::ArrayType
412+
) and
410413
cppType.hasType(t, _) and
411414
base = getTypeImpl(pointerType, indirectionIndex)
412415
|
@@ -478,9 +481,10 @@ private module Cached {
478481
}
479482

480483
/**
481-
* Holds if `iteratorDerefAddress` is an address of an iterator dereference (i.e., `*it`)
482-
* that is used for a write operation that writes the value `value`. The `memory` instruction
483-
* represents the memory that the IR's SSA analysis determined was read by the call to `operator*`.
484+
* Holds if `iteratorAddress` is an address of an iterator (i.e., `it`)
485+
* that is dereferenced and then used for a write operation that writes the value `value`.
486+
* The `memory` instruction represents the memory that the IR's SSA analysis determined was
487+
* read by the call to `operator*`.
484488
*
485489
* The `numberOfLoads` integer represents the number of dereferences this write corresponds to
486490
* on the underlying container that produced the iterator.
@@ -501,6 +505,77 @@ private module Cached {
501505
)
502506
}
503507

508+
private predicate isSource(Instruction instr, Operand iteratorAddress, int numberOfLoads) {
509+
getAUse(instr) = iteratorAddress and
510+
exists(BaseSourceVariableInstruction iteratorBase |
511+
iteratorBase.getResultType() instanceof Interfaces::Iterator and
512+
not iteratorBase.getResultType() instanceof Cpp::PointerType and
513+
isUse(_, iteratorAddress, iteratorBase, numberOfLoads - 1, 0)
514+
)
515+
}
516+
517+
private predicate isSink(Instruction instr, CallInstruction call) {
518+
getAUse(instr).(ArgumentOperand).getCall() = call and
519+
// Don't include various operations that don't modify what the iterator points to.
520+
not exists(Function f | f = call.getStaticCallTarget() |
521+
f instanceof Iterator::IteratorCrementOperator or
522+
f instanceof Iterator::IteratorBinaryArithmeticOperator or
523+
f instanceof Iterator::IteratorAssignArithmeticOperator or
524+
f instanceof Iterator::IteratorCrementMemberOperator or
525+
f instanceof Iterator::IteratorBinaryArithmeticMemberOperator or
526+
f instanceof Iterator::IteratorAssignArithmeticMemberOperator or
527+
f instanceof Iterator::IteratorAssignmentMemberOperator
528+
)
529+
}
530+
531+
private predicate convertsIntoArgumentFwd(Instruction instr) {
532+
isSource(instr, _, _)
533+
or
534+
exists(Instruction prev | convertsIntoArgumentFwd(prev) |
535+
conversionFlow(unique( | | getAUse(prev)), instr, false, _)
536+
)
537+
}
538+
539+
private predicate convertsIntoArgumentRev(Instruction instr) {
540+
convertsIntoArgumentFwd(instr) and
541+
(
542+
isSink(instr, _)
543+
or
544+
exists(Instruction next | convertsIntoArgumentRev(next) |
545+
conversionFlow(unique( | | getAUse(instr)), next, false, _)
546+
)
547+
)
548+
}
549+
550+
private predicate convertsIntoArgument(
551+
Operand iteratorAddress, CallInstruction call, int numberOfLoads
552+
) {
553+
exists(Instruction iteratorAddressDef |
554+
isSource(iteratorAddressDef, iteratorAddress, numberOfLoads) and
555+
isSink(iteratorAddressDef, call) and
556+
convertsIntoArgumentRev(pragma[only_bind_into](iteratorAddressDef))
557+
)
558+
}
559+
560+
private predicate isChiAfterIteratorArgument(
561+
Instruction memory, Operand iteratorAddress, int numberOfLoads
562+
) {
563+
// Ideally, `iteratorAddress` would be an `ArgumentOperand`, but there might be
564+
// various conversions applied to it before it becomes an argument.
565+
// So we do a small amount of flow to find the call that the iterator is passed to.
566+
exists(CallInstruction call | convertsIntoArgument(iteratorAddress, call, numberOfLoads) |
567+
exists(ReadSideEffectInstruction read |
568+
read.getPrimaryInstruction() = call and
569+
read.getSideEffectOperand().getAnyDef() = memory
570+
)
571+
or
572+
exists(LoadInstruction load |
573+
iteratorAddress.getDef() = load and
574+
memory = load.getSourceValueOperand().getAnyDef()
575+
)
576+
)
577+
}
578+
504579
/**
505580
* Holds if `iterator` is a `StoreInstruction` that stores the result of some function
506581
* returning an iterator into an address computed started at `containerBase`.
@@ -521,21 +596,22 @@ private module Cached {
521596
}
522597

523598
/**
524-
* Holds if `iteratorDerefAddress` is an address of an iterator dereference (i.e., `*it`)
525-
* that is used for a read operation. The `memory` instruction represents the memory that
599+
* Holds if `iteratorAddress` is an address of an iterator that is used for
600+
* a read operation. The `memory` instruction represents the memory that
526601
* the IR's SSA analysis determined was read by the call to `operator*`.
527602
*
528-
* Finally, the `numberOfLoads` integer represents the number of dereferences this read
529-
* corresponds to on the underlying container that produced the iterator.
603+
* Finally, the `numberOfLoads` integer represents the number of dereferences
604+
* this read corresponds to on the underlying container that produced the iterator.
530605
*/
531606
private predicate isChiBeforeIteratorUse(
532-
Operand iteratorDerefAddress, Instruction memory, int numberOfLoads
607+
Operand iteratorAddress, Instruction memory, int numberOfLoads
533608
) {
534609
exists(
535610
BaseSourceVariableInstruction iteratorBase, LoadInstruction load,
536-
ReadSideEffectInstruction read
611+
ReadSideEffectInstruction read, Operand iteratorDerefAddress
537612
|
538613
numberOfLoads >= 0 and
614+
isUse(_, iteratorAddress, iteratorBase, numberOfLoads + 1, 0) and
539615
isUse(_, iteratorDerefAddress, iteratorBase, numberOfLoads + 2, 0) and
540616
iteratorBase.getResultType() instanceof Interfaces::Iterator and
541617
load.getSourceAddressOperand() = iteratorDerefAddress and
@@ -566,24 +642,35 @@ private module Cached {
566642
}
567643

568644
/**
569-
* Holds if `iteratorDerefAddress` is an address of an iterator dereference (i.e., `*it`)
570-
* that is used for a read operation to read a value from a container that created the iterator.
571-
* `container` represents the base of the address of the container that was used to create
572-
* the iterator.
645+
* Holds if `iteratorAddress` is an address of an iterator that is used for a
646+
* read operation to read a value from a container that created the iterator.
647+
* `container` represents the base of the address of the container that was used
648+
* to create the iterator.
573649
*/
574650
cached
575651
predicate isIteratorUse(
576-
BaseSourceVariableInstruction container, Operand iteratorDerefAddress, int numberOfLoads,
652+
BaseSourceVariableInstruction container, Operand iteratorAddress, int numberOfLoads,
577653
int indirectionIndex
578654
) {
655+
// Direct use
579656
exists(Instruction begin, Instruction memory, int upper, int ind |
580-
isChiBeforeIteratorUse(iteratorDerefAddress, memory, numberOfLoads) and
657+
isChiBeforeIteratorUse(iteratorAddress, memory, numberOfLoads) and
581658
memorySucc*(begin, memory) and
582659
isChiAfterBegin(container, begin) and
583660
upper = countIndirectionsForCppType(getResultLanguageType(container)) and
584661
ind = numberOfLoads + [1 .. upper] and
585662
indirectionIndex = ind - (numberOfLoads + 1)
586663
)
664+
or
665+
// Use through function output
666+
exists(Instruction memory, Instruction begin, int upper, int ind |
667+
isChiAfterIteratorArgument(memory, iteratorAddress, numberOfLoads) and
668+
memorySucc*(begin, memory) and
669+
isChiAfterBegin(container, begin) and
670+
upper = countIndirectionsForCppType(getResultLanguageType(container)) and
671+
ind = numberOfLoads + [1 .. upper] and
672+
indirectionIndex = ind - (numberOfLoads - 1)
673+
)
587674
}
588675

589676
/** Holds if `op` is the only use of its defining instruction, and that op is used in a conversation */

0 commit comments

Comments
 (0)