@@ -16,6 +16,11 @@ import cpp
1616import codingstandards.cpp.misra
1717import semmle.code.cpp.dataflow.new.TaintTracking
1818import semmle.code.cpp.security.BufferAccess
19+ private import semmle.code.cpp.ir.IR // For PointerArithmeticInstruction (see TrackArray::isAdditionalFlowStep/2 below)
20+ private import semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate // For PointerArithmeticInstruction (see TrackArray::isAdditionalFlowStep/2 below)
21+ private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil // For PointerArithmeticInstruction (see TrackArray::isAdditionalFlowStep/2 below)
22+ private import semmle.code.cpp.ir.dataflow.FlowSteps // For PointerArithmeticInstruction (see TrackArray::isAdditionalFlowStep/2 below)
23+ private import semmle.code.cpp.ir.dataflow.internal.SsaInternals as Ssa
1924
2025class ArrayDeclaration extends VariableDeclarationEntry {
2126 int length ;
@@ -147,6 +152,50 @@ class PointerFormation extends TPointerFormation {
147152 }
148153}
149154
155+ /**
156+ * NOTE
157+ */
158+ private predicate operandToInstructionTaintStep ( Operand opFrom , Instruction instrTo ) {
159+ // Taint can flow through expressions that alter the value but preserve
160+ // more than one bit of it _or_ expressions that follow data through
161+ // pointer indirections.
162+ instrTo .getAnOperand ( ) = opFrom and
163+ (
164+ instrTo instanceof ArithmeticInstruction
165+ or
166+ instrTo instanceof BitwiseInstruction
167+ or
168+ instrTo instanceof PointerArithmeticInstruction
169+ )
170+ or
171+ // Taint flow from an address to its dereference.
172+ Ssa:: isDereference ( instrTo , opFrom , _)
173+ or
174+ // Unary instructions tend to preserve enough information in practice that we
175+ // want taint to flow through.
176+ // The exception is `FieldAddressInstruction`. Together with the rules below for
177+ // `LoadInstruction`s and `ChiInstruction`s, flow through `FieldAddressInstruction`
178+ // could cause flow into one field to come out an unrelated field.
179+ // This would happen across function boundaries, where the IR would not be able to
180+ // match loads to stores.
181+ instrTo .( UnaryInstruction ) .getUnaryOperand ( ) = opFrom and
182+ (
183+ not instrTo instanceof FieldAddressInstruction
184+ or
185+ instrTo .( FieldAddressInstruction ) .getField ( ) .getDeclaringType ( ) instanceof Union
186+ )
187+ or
188+ // Taint from int to boolean casts. This ensures that we have flow to `!x` in:
189+ // ```cpp
190+ // x = integer_source();
191+ // if(!x) { ... }
192+ // ```
193+ exists ( Operand zero |
194+ zero .getDef ( ) .( ConstantValueInstruction ) .getValue ( ) = "0" and
195+ instrTo .( CompareNEInstruction ) .hasOperands ( opFrom , zero )
196+ )
197+ }
198+
150199module TrackArrayConfig implements DataFlow:: ConfigSig {
151200 predicate isSource ( DataFlow:: Node node ) {
152201 exists ( ArrayAllocation arrayAllocation | node = arrayAllocation .getNode ( ) )
@@ -155,9 +204,22 @@ module TrackArrayConfig implements DataFlow::ConfigSig {
155204 predicate isSink ( DataFlow:: Node node ) {
156205 exists ( PointerFormation pointerFormation | node = pointerFormation .getNode ( ) )
157206 }
207+
208+ predicate isAdditionalFlowStep ( DataFlow:: Node nodeFrom , DataFlow:: Node nodeTo ) {
209+ /*
210+ * NOTE
211+ */
212+
213+ operandToInstructionTaintStep ( nodeFrom .asOperand ( ) , nodeTo .asInstruction ( ) )
214+ or
215+ exists ( PointerArithmeticInstruction pai , int indirectionIndex |
216+ nodeHasOperand ( nodeFrom , pai .getAnOperand ( ) , pragma [ only_bind_into ] ( indirectionIndex ) ) and
217+ hasInstructionAndIndex ( nodeTo , pai , indirectionIndex + 1 )
218+ )
219+ }
158220}
159221
160- module TrackArray = TaintTracking :: Global< TrackArrayConfig > ;
222+ module TrackArray = DataFlow :: Global< TrackArrayConfig > ;
161223
162224private predicate arrayIndexExceedsOutOfBounds (
163225 DataFlow:: Node arrayDeclarationNode , DataFlow:: Node pointerFormationNode
0 commit comments