From 5d417d7a69e130cfd9378b35588a25cefb29301b Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Thu, 8 Dec 2022 17:58:20 +0000 Subject: [PATCH] C++: Implement an 'Indirection' subtype for iterators. --- .../dataflow/internal/SsaInternalsCommon.qll | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaInternalsCommon.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaInternalsCommon.qll index 5ce95b30342..48c62b9c109 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaInternalsCommon.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaInternalsCommon.qll @@ -188,6 +188,93 @@ private class PointerOrReferenceTypeIndirection extends Indirection instanceof P override predicate isAdditionalWrite(Node0Impl value, Operand address, boolean certain) { none() } } +private module IteratorIndirections { + import semmle.code.cpp.models.interfaces.Iterator as Interfaces + import semmle.code.cpp.models.implementations.Iterator as Iterator + import semmle.code.cpp.models.implementations.StdContainer as StdContainer + + class IteratorIndirection extends Indirection instanceof Interfaces::Iterator { + IteratorIndirection() { + not this instanceof PointerOrReferenceTypeIndirection and baseType = super.getValueType() + } + + override int getNumberOfIndirections() { result = 1 + countIndirections(this.getBaseType()) } + + override predicate isAdditionalDereference(Instruction deref, Operand address) { + exists(CallInstruction call | + operandForfullyConvertedCall(deref.getAUse(), call) and + this = call.getStaticCallTarget().getClassAndName("operator*") and + address = call.getThisArgumentOperand() + ) + } + + override predicate isAdditionalWrite(Node0Impl value, Operand address, boolean certain) { + exists(CallInstruction call | call.getArgumentOperand(0) = value.asOperand() | + this = call.getStaticCallTarget().getClassAndName("operator=") and + address = call.getThisArgumentOperand() and + certain = false + ) + } + + override predicate isAdditionalTaintStep(Node node1, Node node2) { + exists(CallInstruction call | + // Taint through `operator+=` and `operator-=` on iterators. + call.getStaticCallTarget() instanceof Iterator::IteratorAssignArithmeticOperator and + node2.(IndirectArgumentOutNode).getPreUpdateNode() = node1 and + node1.(IndirectOperand).getOperand() = call.getArgumentOperand(0) and + node1.getType().getUnspecifiedType() = this + ) + } + + override predicate isAdditionalConversionFlow(Operand opFrom, Instruction instrTo) { + // This is a bit annoying: Consider the following snippet: + // ``` + // struct MyIterator { + // ... + // insert_iterator_by_trait operator*(); + // insert_iterator_by_trait operator=(int x); + // }; + // ... + // MyIterator it; + // ... + // *it = source(); + // ``` + // The qualifier to `operator*` will undergo prvalue-to-xvalue conversion and a + // temporary object will be created. Thus, the IR for the call to `operator=` will + // look like (simplified): + // ``` + // r1(glval) = VariableAddress[it] : + // r2(glval) = FunctionAddress[operator*] : + // r3(MyIterator) = Call[operator*] : func:r2, this:r1 + // r4(glval) = VariableAddress[#temp] : + // m1(MyIterator) = Store[#temp] : &:r4, r3 + // r5(glval) = FunctionAddress[operator=] : + // r6(glval) = FunctionAddress[source] : + // r7(int) = Call[source] : func:r6 + // r8(MyIterator) = Call[operator=] : func:r5, this:r4, 0:r7 + // ``` + // in order to properly recognize that the qualifier to the call to `operator=` accesses + // `it` we look for the store that writes to the temporary object, and use the source value + // of that store as the "address" to continue searching for the base variable `it`. + exists(StoreInstruction store, VariableInstruction var | + var = instrTo and + var.getIRVariable() instanceof IRTempVariable and + opFrom.getType() = this and + store.getSourceValueOperand() = opFrom and + store.getDestinationAddress() = var + ) + or + // A call to `operator++` or `operator--` is the iterator equivalent version of a + // pointer arithmetic instruction. + exists(CallInstruction call | + instrTo = call and + call.getStaticCallTarget() instanceof Iterator::IteratorCrementMemberOperator and + opFrom = call.getThisArgumentOperand() + ) + } + } +} + predicate isDereference(Instruction deref, Operand address) { any(Indirection ind).isAdditionalDereference(deref, address) or